Projects overview

* Added projects overview
* Added modal for new project
* Added error codes and validation for new project path
projects
Domeniko Gentner 3 years ago
parent 76cf02a1f0
commit 285a808ee4
  1. 43
      labertasche/blueprints/bp_dashboard.py
  2. 12
      labertasche/helper/__init__.py
  3. 2
      static/css/labertasche.css
  4. 16
      static/css/sass/labertasche.scss
  5. 68
      static/js/dashboard.js
  6. 120
      templates/project_overview.html

@ -6,11 +6,11 @@
# * _repo : https://git.tuxstash.de/gothseidank/labertasche # * _repo : https://git.tuxstash.de/gothseidank/labertasche
# * _license : This project is under MIT License # * _license : This project is under MIT License
# *********************************************************************************/ # *********************************************************************************/
from flask import Blueprint, render_template, request, redirect from flask import Blueprint, render_template, request, redirect, make_response, jsonify
from flask_login import login_required from flask_login import login_required
from labertasche.database import labertasche_db as db from labertasche.database import labertasche_db as db
from labertasche.models import TLocation, TComments, TEmail from labertasche.models import TLocation, TComments, TEmail, TProjects
from labertasche.helper import dates_of_the_week, export_location from labertasche.helper import dates_of_the_week, export_location, get_id_from_project_name
from sqlalchemy import func from sqlalchemy import func
import re import re
@ -18,15 +18,48 @@ import re
bp_dashboard = Blueprint("bp_dashboard", __name__, url_prefix='/dashboard') bp_dashboard = Blueprint("bp_dashboard", __name__, url_prefix='/dashboard')
@bp_dashboard.route('/') @bp_dashboard.route("/")
@login_required @login_required
def dashboard_index(): def dashboard_index():
t_projects = db.session.query(TProjects).all()
projects = list()
for each in t_projects:
comments = db.session.query(TComments).filter(TComments.project_id == each.id_project) \
.filter(TComments.is_published == True) \
.filter(TComments.is_spam == False).count()
unpub_comments = db.session.query(TComments).filter(TComments.project_id == each.id_project) \
.filter(TComments.is_spam == False) \
.filter(TComments.is_published == False).count()
spam = db.session.query(TComments).filter(TComments.project_id == each.id_project) \
.filter(TComments.is_spam == True).count()
projects.append(dict({
"id_project": each.id_project,
"name": each.name,
"total_comments": comments,
"total_spam": spam,
"total_unpublished": unpub_comments
}))
return render_template('project_overview.html', projects=projects)
@bp_dashboard.route("/project/new", methods=['POST'])
@login_required
def dashboard_new_project():
return make_response(jsonify(status='too-short'), 200)
@bp_dashboard.route('/<project>/')
@login_required
def dashboard_project_index(project: str):
proj_id = get_id_from_project_name(project)
dates = dates_of_the_week() dates = dates_of_the_week()
spam = list() spam = list()
published = list() published = list()
unpublished = list() unpublished = list()
for each in dates: for each in dates:
spam_comments = db.session.query(TComments).filter(func.DATE(TComments.created_on) == each.date())\ spam_comments = db.session.query(TComments).filter(TComments.project_id == proj_id)\
.filter(func.DATE(TComments.created_on) == each.date())\
.filter(TComments.is_spam == True).all() .filter(TComments.is_spam == True).all()
pub_comments = db.session.query(TComments).filter(func.DATE(TComments.created_on) == each.date()) \ pub_comments = db.session.query(TComments).filter(func.DATE(TComments.created_on) == each.date()) \

@ -8,7 +8,7 @@
# *********************************************************************************/ # *********************************************************************************/
import datetime import datetime
import json import json
from labertasche.models import TLocation, TComments from labertasche.models import TLocation, TComments, TProjects
from labertasche.settings import Settings from labertasche.settings import Settings
from labertasche.database import labertasche_db as db from labertasche.database import labertasche_db as db
from functools import wraps from functools import wraps
@ -192,3 +192,13 @@ def dates_of_the_week():
date_list.append(monday) date_list.append(monday)
date_list.append((monday + datetime.timedelta(days=1, hours=23, minutes=59, seconds=59))) date_list.append((monday + datetime.timedelta(days=1, hours=23, minutes=59, seconds=59)))
return date_list return date_list
def get_id_from_project_name(name: str) -> int:
"""
Returns the id of a project name
:param name: The display name of the project
:return: the ID of the project
"""
proj = db.session.query(TProjects).filter(TProjects.name == name).first()
return proj.id_project

File diff suppressed because one or more lines are too long

@ -23,7 +23,10 @@ $navbar-burger-color: black;
$footer-background-color: #393f4d; $footer-background-color: #393f4d;
$footer-color: true; $footer-color: true;
$card-header-color: white; $card-header-color: black;
$card-header-background-color: #feda6a;
$card-footer-background-color: #feda6a;
$card-background-color: grey;
$table-head-background-color: #feda6a; $table-head-background-color: #feda6a;
$table-body-background-color: white; $table-body-background-color: white;
@ -78,6 +81,11 @@ canvas{
border: 2px solid #feda6a; border: 2px solid #feda6a;
} }
.brdr-darkslate{
border: 2px solid #1d1e22;
}
.bg-compliment{ .bg-compliment{
background-color: #384667; background-color: #384667;
} }
@ -85,3 +93,9 @@ canvas{
.fg-yellow{ .fg-yellow{
color: #feda6a; color: #feda6a;
} }
.my-shadow-subtle{
-webkit-box-shadow: 5px 5px 5px 0 rgba(0,0,0,0.75);
-moz-box-shadow: 5px 5px 5px 0 rgba(0,0,0,0.75);
box-shadow: 5px 5px 5px 0 rgba(0,0,0,0.75);
}

@ -19,3 +19,71 @@ function dashboard_mailsearch(search_txt)
} }
} }
} }
function new_project_save() {
let modal_ok = document.getElementById('modal-ok');
let modal_cancel = document.getElementById('modal-cancel');
let short_help = document.getElementById('new-project-too-short');
let short_help_invalid = document.getElementById('new-project-invalid-name');
let name = document.getElementById('project-name').value
short_help.classList.add('is-hidden');
short_help_invalid.classList.add('is-hidden');
// Validate input
if (name.length === 0) {
short_help.classList.remove('is-hidden');
return false;
}
if (/^\w+$/.test(name) === false){
short_help_invalid.classList.remove('is-hidden');
return false;
}
modal_ok.classList.add('is-loading');
modal_cancel.classList.add('is-hidden');
fetch(window.location.protocol + "//" + window.location.host + '/dashboard/project/new',
{
mode: "cors",
headers: {
'Access-Control-Allow-Origin': '*',
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
// use real location
body: JSON.stringify({
"name": name
})
})
.then(async function (response) {
let result = await response.json();
result = result['status'];
modal_ok.classList.remove('is-loading');
modal_cancel.classList.remove('is-hidden');
if (result === "ok"){
hide_modal('modal-new-project');
}
if (result === "too-short"){
short_help.classList.remove('is-hidden');
}
if (result === "invalid-name"){
short_help_invalid.classList.remove('is-hidden');
}
})
.catch(function (exc) {
console.log(exc);
})
}
function hide_modal(id_name)
{
let el = document.getElementById(id_name);
el.classList.remove("is-active");
}
function show_modal(id_name)
{
let el = document.getElementById(id_name);
el.classList.add("is-active");
}

@ -0,0 +1,120 @@
{% extends "base.html" %}
{% block main %}
<div style="min-height: 100vh;" class="container bg-deepmatte p-6 brdr-yayellow">
<h1 class="is-size-2 mb-3 is-uppercase">Select project</h1>
<div class="columns is-multiline">
<div class="column is-4">
<div class="card my-shadow-subtle brdr-darkslate">
<div class="card-header">
<div class="card-header-title">
<p class="is-size-4 is-uppercase">
NEW PROJECT
</p>
</div>
</div>
<div class="card-content has-text-white">
<div class="level is-mobile">
<div class="level-item has-text-centered">
<div>
<p class="heading is-capitalized">Comments</p>
<p class="is-size-4 has-text-weight-bold has-text-white">n/a</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading is-capitalized">Unpublished</p>
<p class="is-size-4 has-text-weight-bold has-text-white">n/a</p>
</div>
</div>
<div class="level-item has-text-centered">
<div class="ml-4">
<p class="heading is-capitalized">Spam</p>
<p class="is-size-4 has-text-weight-bold has-text-white">n/a</p>
</div>
</div>
</div>
</div>
<div class="card-footer">
<div class="card-footer-item">
<a class="has-text-weight-bold has-text-black is-uppercase"
onclick="show_modal('modal-new-project');"
href="#">NEW</a>
</div>
</div>
</div>
</div>
{% for project in projects %}
<div class="column is-4">
<div class="card my-shadow-subtle brdr-darkslate">
<div class="card-header">
<div class="card-header-title">
<p class="is-size-4 is-uppercase">
{{ project['name'] }}
</p>
</div>
</div>
<div class="card-content has-text-white">
<div class="level is-mobile">
<div class="level-item has-text-centered">
<div>
<p class="heading is-capitalized">Comments</p>
<p class="is-size-4 has-text-weight-bold has-text-white">{{ project['total_comments'] }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading is-capitalized">Unpublished</p>
<p class="is-size-4 has-text-weight-bold has-text-white">{{ project['total_unpublished'] }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div class="ml-4">
<p class="heading is-capitalized">Spam</p>
<p class="is-size-4 has-text-weight-bold has-text-white">{{ project['total_spam'] }}</p>
</div>
</div>
</div>
</div>
<div class="card-footer">
<div class="card-footer-item">
<a class="has-text-weight-bold has-text-black is-uppercase" href="#">EDIT</a>
</div>
<div class="card-footer-item">
<a class="has-text-weight-bold has-text-black is-uppercase" href="#">DELETE</a>
</div>
<div class="card-footer-item">
<a class="has-text-weight-bold has-text-black is-uppercase"
href="{{ url_for('bp_dashboard.dashboard_project_index', project=project['name'])}}">VIEW</a>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<div class="modal is-active" id="modal-new-project">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">New Project</p>
<button class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body">
<label for="project-name" class="has-text-black">PROJECT NAME
<input class="input is-success"
type="text"
name="project-name"
id="project-name"
placeholder="Type a name without special characters.."
>
</label>
<p id="new-project-too-short" class="is-hidden help has-text-danger">Input too short. Needs at least 1 character!</p>
<p id="new-project-invalid-name" class="is-hidden help has-text-danger">Input is invalid. Please use only a-z and 0-9.</p>
</section>
<footer class="modal-card-foot">
<button id="modal-ok" onclick="new_project_save()" class="button is-success">Save</button>
<button id="modal-cancel" onclick="hide_modal('modal-new-project')" class="button is-danger">Cancel</button>
</footer>
</div>
</div>
{% endblock %}
Loading…
Cancel
Save