Compare commits
No commits in common. "master" and "fe_init" have entirely different histories.
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
# Created by https://www.toptal.com/developers/gitignore/api/python,flask,vim,visualstudiocode
|
# Created by https://www.toptal.com/developers/gitignore/api/python,flask,vim,visualstudiocode
|
||||||
# Edit at https://www.toptal.com/developers/gitignore?templates=python,flask,vim,visualstudiocode
|
# Edit at https://www.toptal.com/developers/gitignore?templates=python,flask,vim,visualstudiocode
|
||||||
*.db
|
*.db
|
||||||
@ -23,7 +24,7 @@ dist/
|
|||||||
downloads/
|
downloads/
|
||||||
eggs/
|
eggs/
|
||||||
.eggs/
|
.eggs/
|
||||||
#lib/
|
lib/
|
||||||
lib64/
|
lib64/
|
||||||
parts/
|
parts/
|
||||||
sdist/
|
sdist/
|
||||||
@ -233,5 +234,3 @@ tags
|
|||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/python,flask,vim,visualstudiocode
|
# End of https://www.toptal.com/developers/gitignore/api/python,flask,vim,visualstudiocode
|
||||||
|
|
||||||
# deb files
|
|
||||||
*.deb
|
|
||||||
|
2
LICENSE
2
LICENSE
@ -219,7 +219,7 @@ If you develop a new program, and you want it to be of the greatest possible use
|
|||||||
|
|
||||||
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
|
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
taskmanager
|
flaskapp
|
||||||
Copyright (C) 2023 Decentrala
|
Copyright (C) 2023 Decentrala
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
19
Makefile
19
Makefile
@ -1,19 +0,0 @@
|
|||||||
VENV = venv
|
|
||||||
PYTHON = $(VENV)/bin/python3
|
|
||||||
PIP = $(VENV)/bin/pip
|
|
||||||
|
|
||||||
run: $(VENV)/bin/activate
|
|
||||||
$(PYTHON) init_db.py
|
|
||||||
$(PYTHON) run.py
|
|
||||||
|
|
||||||
|
|
||||||
$(VENV)/bin/activate: requirements.txt
|
|
||||||
python3 -m venv $(VENV)
|
|
||||||
$(PIP) install -r requirements.txt
|
|
||||||
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf __pycache__
|
|
||||||
rm -rf $(VENV)
|
|
||||||
rm -r taskmanager/__pycache__/
|
|
||||||
rm instance/taskmanager.db
|
|
39
README.md
39
README.md
@ -1,39 +1,4 @@
|
|||||||
# Task Manager
|
# flaskapp
|
||||||
|
|
||||||
Interactive TODO list web application
|
Web app
|
||||||
|
|
||||||
# Development Setup
|
|
||||||
|
|
||||||
Install python and pip on local machine
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install virtualenv
|
|
||||||
python -m venv venv #/path/to/new/virtual/environment
|
|
||||||
source venv/bin/activate #activate virtual env
|
|
||||||
pip install -r requirments.txt
|
|
||||||
python3 ./init_db.py #initialize database
|
|
||||||
|
|
||||||
python3 ./run.py #run project
|
|
||||||
```
|
|
||||||
|
|
||||||
# On database changes
|
|
||||||
|
|
||||||
Delete file `/instance/taskmanager.db`
|
|
||||||
And reinit the db
|
|
||||||
|
|
||||||
```shell
|
|
||||||
python3 ./init_db.py
|
|
||||||
```
|
|
||||||
|
|
||||||
# Build app
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd build-deb/
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
# Install app
|
|
||||||
|
|
||||||
```bash
|
|
||||||
apt install ./build-deb/taskmanager.deb
|
|
||||||
```
|
|
||||||
|
@ -7,12 +7,8 @@ man: man/taskmanager.1.md
|
|||||||
deb: man ../requirments.txt ../run.py ../taskmanager ../LICENSE
|
deb: man ../requirments.txt ../run.py ../taskmanager ../LICENSE
|
||||||
cp -r ../taskmanager/* taskmanager/var/taskmanager/taskmanager/
|
cp -r ../taskmanager/* taskmanager/var/taskmanager/taskmanager/
|
||||||
cp ../run.py taskmanager/var/taskmanager/
|
cp ../run.py taskmanager/var/taskmanager/
|
||||||
cp ../init_db.py taskmanager/var/taskmanager/
|
|
||||||
cp ../LICENSE taskmanager/var/taskmanager/
|
cp ../LICENSE taskmanager/var/taskmanager/
|
||||||
chmod -w taskmanager/DEBIAN/*
|
|
||||||
chmod +w taskmanager/DEBIAN/control
|
|
||||||
dpkg-deb --build taskmanager
|
dpkg-deb --build taskmanager
|
||||||
chmod +w taskmanager/DEBIAN/*
|
|
||||||
clean:
|
clean:
|
||||||
rm -f taskmanager.deb
|
rm -f taskmanager.deb
|
||||||
rm -f man/taskmanager.1
|
rm -f man/taskmanager.1
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
% TASKMANAGER(1) taskmanager 1.0.0
|
% FLASKAPP(1) taskmanager 1.0.0
|
||||||
% Decentrala
|
% Decentrala
|
||||||
% Jun 2023
|
% Jun 2023
|
||||||
|
|
||||||
# NAME
|
# NAME
|
||||||
taskmanager - Interactive TODO list Web app
|
taskmanager - Web app
|
||||||
|
|
||||||
# SYNOPSIS
|
# SYNOPSIS
|
||||||
**python3 run.py**
|
**python3 run.py**
|
||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
Interactive TODO list Web app
|
Web app
|
||||||
|
|
||||||
# AUTHORS
|
# AUTHORS
|
||||||
Decentrala
|
Decentrala
|
@ -7,5 +7,5 @@ Installed-Size: 2000
|
|||||||
Depends: gunicorn, python3-flask-sqlalchemy
|
Depends: gunicorn, python3-flask-sqlalchemy
|
||||||
Homepage: https://gitea.dmz.rs/Decentrala/taskmanager
|
Homepage: https://gitea.dmz.rs/Decentrala/taskmanager
|
||||||
Maintainer: Decentrala <dmz@dmz.rs>
|
Maintainer: Decentrala <dmz@dmz.rs>
|
||||||
Description: Interactive TODO list Web app
|
Description: Web app
|
||||||
Version: 1.0.10
|
Version: 1.0.0
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
/usr/bin/systemctl enable taskmanager.service
|
/usr/bin/systemctl enable taskmanager.service
|
||||||
/var/taskmanager/init_db.py
|
|
||||||
/sbin/service taskmanager start
|
/sbin/service taskmanager start
|
||||||
if [ -f /tmp/oldtaskmanagerconfig.ini ] ; then
|
|
||||||
cp /tmp/oldtaskmanagerconfig.ini /var/taskmanager/taskmanager/config.ini
|
|
||||||
rm /tmp/oldtaskmanagerconfig.ini
|
|
||||||
fi
|
|
||||||
if [ -f /tmp/oldtaskmanager.db ] ; then
|
|
||||||
cp /tmp/oldtaskmanager.db /var/taskmanager/instance/taskmanager.db
|
|
||||||
rm /tmp/oldtaskmanager.db
|
|
||||||
fi
|
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
if [ -f /var/taskmanager/taskmanager/config.ini ] ; then
|
|
||||||
cp /var/taskmanager/taskmanager/config.ini /tmp/oldtaskmanagerconfig.ini
|
|
||||||
fi
|
|
||||||
if [ -f /var/taskmanager/instance/taskmanager.db ] ; then
|
|
||||||
cp /var/taskmanager/instance/taskmanager.db /tmp/oldtaskmanager.db
|
|
||||||
fi
|
|
@ -1,9 +1,3 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
/sbin/service taskmanager stop
|
/sbin/service taskmanager stop
|
||||||
/usr/bin/systemctl disable taskmanager.service
|
/usr/bin/systemdctl disable taskmanager.service
|
||||||
if [ -f /var/taskmanager/taskmanager/config.ini ] ; then
|
|
||||||
cp /var/taskmanager/taskmanager/config.ini /tmp/oldtaskmanagerconfig.ini
|
|
||||||
fi
|
|
||||||
if [ -f /var/taskmanager/instance/taskmanager.db ] ; then
|
|
||||||
cp /var/taskmanager/instance/taskmanager.db /tmp/oldtaskmanager.db
|
|
||||||
fi
|
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Gunicorn taskmanager service
|
|
||||||
Documentation=man:gunicorn(1)
|
|
||||||
After=network.target nss-lookup.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
WorkingDirectory=/var/taskmanager/
|
|
||||||
ExecStart=/usr/bin/gunicorn --workers 3 --bind 0.0.0.0:80 run:app
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from taskmanager import db, app
|
from flaskapp import db
|
||||||
|
|
||||||
print('[i] Trying to create databse...')
|
print('[i] Trying to create databse...')
|
||||||
try:
|
try:
|
||||||
with app.app_context():
|
db.create_all()
|
||||||
db.create_all()
|
|
||||||
print('[+] Success you can proceed with deployment!')
|
print('[+] Success you can proceed with deployment!')
|
||||||
except:
|
except:
|
||||||
print('[-] Creating db failed :/')
|
print('[-] Creating db failed :/')
|
||||||
|
3
run.py
3
run.py
@ -1,5 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
from flaskapp import app
|
||||||
from taskmanager import app
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(debug=False)
|
app.run(debug=False)
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
[credentials]
|
|
||||||
ADMINPASS = defaultpassword
|
|
@ -1,17 +0,0 @@
|
|||||||
from taskmanager.models import *
|
|
||||||
|
|
||||||
def gettaskusers(taskid):
|
|
||||||
users = list()
|
|
||||||
userids = list()
|
|
||||||
try:
|
|
||||||
taskusers = TaskUser.query.filter_by(taskid = taskid).all()
|
|
||||||
except:
|
|
||||||
taskusers = list()
|
|
||||||
|
|
||||||
for taskuser in taskusers:
|
|
||||||
userids.append(taskuser.userid)
|
|
||||||
|
|
||||||
for userid in userids:
|
|
||||||
users.append(User.query.get(userid))
|
|
||||||
|
|
||||||
return users
|
|
@ -1,19 +1,7 @@
|
|||||||
from taskmanager import db
|
from taskmanager import db
|
||||||
|
|
||||||
class Task(db.Model):
|
class Table(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
name = db.Column(db.String, nullable=False)
|
variable1 = db.Column(db.Integer, nullable=False)
|
||||||
desc = db.Column(db.String, nullable=True)
|
variable2 = db.Column(db.Integer, nullable=False)
|
||||||
creatorid = db.Column(db.Integer, nullable=True)
|
|
||||||
|
|
||||||
class User(db.Model):
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
|
||||||
username = db.Column(db.String, nullable=False)
|
|
||||||
contact = db.Column(db.String, nullable=False)
|
|
||||||
password = db.Column(db.String, nullable=True)
|
|
||||||
|
|
||||||
class TaskUser(db.Model):
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
|
||||||
taskid = db.Column(db.Integer, nullable=False)
|
|
||||||
userid = db.Column(db.Integer, nullable=False)
|
|
||||||
|
|
||||||
|
@ -1,184 +1,34 @@
|
|||||||
import configparser
|
|
||||||
import os
|
|
||||||
|
|
||||||
from flask import render_template, request, redirect
|
from flask import render_template, request, redirect
|
||||||
|
|
||||||
from taskmanager import app, db
|
from taskmanager import app, db
|
||||||
from taskmanager.functions import *
|
from taskmanager.functions import *
|
||||||
from taskmanager.models import *
|
from taskmanager.models import *
|
||||||
|
|
||||||
PROJECT_PATH = os.path.abspath(os.path.dirname(__file__))
|
|
||||||
CONFIG_PATH = os.path.join(PROJECT_PATH, "config.ini")
|
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config.read(CONFIG_PATH)
|
|
||||||
ADMINPASS = config.get('credentials', 'ADMINPASS')
|
|
||||||
|
|
||||||
@app.route('/', methods=['GET'])
|
@app.route('/', methods=['GET'])
|
||||||
def index():
|
def index():
|
||||||
tasks = Task.query.all()
|
|
||||||
return render_template('pages/index.html', tasks = tasks)
|
|
||||||
|
|
||||||
@app.route('/addtask', methods=['GET','POST'])
|
|
||||||
def addtask():
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
return render_template('pages/addtask.html')
|
localvariable1 = "Placeholder1"
|
||||||
elif request.method == 'POST':
|
localvariable1 = "Placeholder2"
|
||||||
taskname = request.form['taskname']
|
|
||||||
taskdesc = request.form['taskdesc']
|
|
||||||
username = request.form['username']
|
|
||||||
# Input sanitation
|
|
||||||
# Task name
|
|
||||||
if not taskname.isprintable() or ("<" in taskname and ">" in taskname):
|
|
||||||
return render_template('pages/response.html', response = "Task name has to be made only of letters or numbers.")
|
|
||||||
if len(taskname) < 1 or len(taskname) > 40:
|
|
||||||
return render_template('pages/response.html', response = "Task name lenght invalid, only smaller then 40 charachters allowed")
|
|
||||||
|
|
||||||
# Username
|
|
||||||
if username == "":
|
|
||||||
creatorid = None
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
creatorid = User.query.filter_by(username = username).first().id
|
|
||||||
except:
|
|
||||||
return render_template('pages/response.html', response = 'No user with this username. Please register')
|
|
||||||
if creatorid is None:
|
|
||||||
return render_template('pages/response.html', response = 'No user with this username. Please register.')
|
|
||||||
|
|
||||||
# Task descripton
|
|
||||||
if taskdesc != '':
|
|
||||||
if not taskdesc.isprintable() or ("<" in taskdesc and ">" in taskdesc):
|
|
||||||
return render_template('pages/response.html', response = "Task description has to be made of printable characters.")
|
|
||||||
if len(taskdesc) > 2000:
|
|
||||||
return render_template('pages/response.html', response = "Task description lenght invalid, only smaller then 2000 charachters allowed")
|
|
||||||
sqladdtask = Task(name = taskname, desc = taskdesc, creatorid = creatorid)
|
|
||||||
try:
|
try:
|
||||||
db.session.add(sqladdtask)
|
return render_template('index.html', pagevariable1 = localvariable1, pagevariable2 = localvariable2 )
|
||||||
db.session.commit()
|
|
||||||
return render_template('pages/response.html', response = 'Task added')
|
|
||||||
except:
|
except:
|
||||||
return render_template('pages/response.html', response = 'Adding task failed')
|
return 'Error retriving page'
|
||||||
|
else:
|
||||||
|
return 'HTTP request method not recogniezed'
|
||||||
|
|
||||||
@app.route('/register', methods=['POST', 'GET'])
|
@app.route('/submit', methods=['POST', 'GET'])
|
||||||
def register():
|
def submit():
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
return render_template('pages/register.html')
|
return render_template('submit.html')
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
username = request.form['username']
|
userinput1 = request.form['forminput1']
|
||||||
contact = request.form['contact']
|
userinput2 = request.form['forminput2']
|
||||||
password = request.form['password']
|
sqlrow = Table(variable1 = int(userinput1), variable2 = int(userinput2))
|
||||||
|
|
||||||
# Username
|
|
||||||
if not username.isalnum():
|
|
||||||
return render_template('pages/response.html', response = "Username has to be made only of letters or numbers.")
|
|
||||||
if len(username) < 1 or len(username) > 40:
|
|
||||||
return render_template('pages/response.html', response = "Username lenght invalid, only smaller then 40 charachters allowed")
|
|
||||||
|
|
||||||
# Contact
|
|
||||||
if contact != '':
|
|
||||||
if not contact.isprintable() or ("<" in contact and ">" in contact):
|
|
||||||
return render_template('pages/response.html', response = "Contact information has to be made of printable characters.")
|
|
||||||
if len(contact) > 100:
|
|
||||||
return render_template('pages/response.html', response = "Contact lenght invalid, only smaller then 100 charachters allowed")
|
|
||||||
|
|
||||||
# Password
|
|
||||||
if password != '':
|
|
||||||
if not password.isprintable():
|
|
||||||
return render_template('pages/response.html', response = "Password has to be made of printable characters.")
|
|
||||||
if len(password) > 500:
|
|
||||||
return render_template('pages/response.html', response = "Password lenght invalid, only smaller then 500 charachters allowed")
|
|
||||||
|
|
||||||
sqladduser = User(username = username, contact = contact, password = password)
|
|
||||||
try:
|
try:
|
||||||
db.session.add(sqladduser)
|
db.session.add(sqlrow)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return render_template('pages/response.html', response = 'User added')
|
return 'Row added'
|
||||||
except:
|
except:
|
||||||
return render_template('pages/response.html', response = 'Adding user failed')
|
return 'Adding row to table failed'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return render_template('pages/response.html', response = 'HTTP request method not recogniezed')
|
return 'HTTP request method not recogniezed'
|
||||||
|
|
||||||
|
|
||||||
@app.route('/projects/<int:task_id>', methods=['GET','POST'])
|
|
||||||
def project(task_id:int):
|
|
||||||
try:
|
|
||||||
task = Task.query.get(task_id)
|
|
||||||
except:
|
|
||||||
return render_template('pages/response.html', response = 'Task not found, bad URL')
|
|
||||||
if task is None:
|
|
||||||
return render_template('pages/response.html', response = 'Task not found, bad URL')
|
|
||||||
users = gettaskusers(task_id)
|
|
||||||
if request.method == 'GET':
|
|
||||||
return render_template("pages/project.html", task = task, users = users)
|
|
||||||
elif request.method == 'POST':
|
|
||||||
# Assigning user to task
|
|
||||||
username = request.form['username']
|
|
||||||
for user in users:
|
|
||||||
if username == user.username:
|
|
||||||
return render_template('pages/response.html', response = 'User already added to task')
|
|
||||||
try:
|
|
||||||
userid = User.query.filter_by(username = username).first().id
|
|
||||||
except:
|
|
||||||
return render_template('pages/response.html', response = 'User not found, please register.')
|
|
||||||
if userid is None:
|
|
||||||
return render_template('pages/response.html', response = 'User not found, please register.')
|
|
||||||
sqladduser = TaskUser(userid = userid, taskid = task_id)
|
|
||||||
try:
|
|
||||||
db.session.add(sqladduser)
|
|
||||||
db.session.commit()
|
|
||||||
return render_template('pages/response.html', response = 'User added')
|
|
||||||
except:
|
|
||||||
return render_template('pages/response.html', response = 'Adding user failed')
|
|
||||||
|
|
||||||
@app.route('/projects/<int:task_id>/del', methods=['GET','POST'])
|
|
||||||
def deltask(task_id:int):
|
|
||||||
try:
|
|
||||||
task = Task.query.get(task_id)
|
|
||||||
except:
|
|
||||||
return render_template('pages/response.html', response = 'Task not found, bad URL')
|
|
||||||
if task is None:
|
|
||||||
return render_template('pages/response.html', response = 'Task not found, bad URL')
|
|
||||||
try:
|
|
||||||
taskusers = TaskUser.query.filter_by(taskid = task_id).all()
|
|
||||||
except:
|
|
||||||
taskusers = None
|
|
||||||
creatorid = task.creatorid
|
|
||||||
if request.method == 'GET':
|
|
||||||
if creatorid is None:
|
|
||||||
try:
|
|
||||||
db.session.delete(task)
|
|
||||||
db.session.commit()
|
|
||||||
except:
|
|
||||||
return render_template('pages/response.html', response = 'Deleting task failed')
|
|
||||||
try:
|
|
||||||
if taskusers != None:
|
|
||||||
for taskuser in taskusers:
|
|
||||||
db.session.delete(taskuser)
|
|
||||||
db.session.commit()
|
|
||||||
except:
|
|
||||||
return render_template('pages/response.html', response = 'Deleting user assignment to task failed')
|
|
||||||
return render_template('pages/response.html', response = 'Task deleted')
|
|
||||||
else:
|
|
||||||
return render_template('pages/deltask.html', task = task)
|
|
||||||
if request.method == 'POST':
|
|
||||||
password = request.form['password']
|
|
||||||
if len(password) < 1 or len(password) > 500:
|
|
||||||
return render_template('pages/response.html', response = "Password lenght invalid, only smaller then 500 charachters allowed")
|
|
||||||
# Check password
|
|
||||||
if password != ADMINPASS and password != User.query.get(creatorid).password:
|
|
||||||
return render_template('pages/response.html', response = 'Wrong password')
|
|
||||||
# Delete task
|
|
||||||
try:
|
|
||||||
db.session.delete(task)
|
|
||||||
db.session.commit()
|
|
||||||
except:
|
|
||||||
return render_template('pages/response.html', response = 'Deleting task failed')
|
|
||||||
try:
|
|
||||||
if taskusers != None:
|
|
||||||
for taskuser in taskusers:
|
|
||||||
db.session.delete(taskuser)
|
|
||||||
db.session.commit()
|
|
||||||
except:
|
|
||||||
return render_template('pages/response.html', response = 'Deleting user assignment to task failed')
|
|
||||||
return render_template('pages/response.html', response = 'Task deleted')
|
|
||||||
|
@ -1,27 +1,16 @@
|
|||||||
:root {
|
:root {
|
||||||
--border-radus: 1rem;
|
--border-radus: 1rem;
|
||||||
--background: #383840;
|
--background: #000000;
|
||||||
--header-background: #212121;
|
--header-background: #FFFFFF;
|
||||||
--header-height: 3rem;
|
--header-height: 3rem;
|
||||||
--input-bar-height: 3rem;
|
--input-bar-height: 3rem;
|
||||||
--white: #FFF;
|
|
||||||
--primary: #d2d2d2;
|
|
||||||
--font-primary: #d2d2d2;
|
|
||||||
color: #FFF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
body{
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, a:visited {
|
|
||||||
color: var(--font-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -30,7 +19,6 @@ main {
|
|||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 30px 0;
|
|
||||||
height: calc(100vh - var(--header-height) - var(--input-bar-height));
|
height: calc(100vh - var(--header-height) - var(--input-bar-height));
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
@ -47,12 +35,6 @@ header {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
header ul {
|
|
||||||
display: flex;
|
|
||||||
list-style-type: none;
|
|
||||||
gap:15px
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: var(--input-bar-height);
|
height: var(--input-bar-height);
|
||||||
@ -64,120 +46,3 @@ footer {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* global classes */
|
|
||||||
.container {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 960px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 20px 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page h1 {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-wrap {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 15px 12px;
|
|
||||||
}
|
|
||||||
.btn {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 5px 20px;
|
|
||||||
border: 1px solid var(--primary);
|
|
||||||
color: var(--primary);
|
|
||||||
background-color: transparent;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover {
|
|
||||||
background-color: rgba(0,0,0,0.3);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 11px;
|
|
||||||
display: inline-block;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.underline {
|
|
||||||
border-bottom: 1px solid var(--font-primary);
|
|
||||||
padding-bottom: 2px;
|
|
||||||
margin-top:20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-tasks > ul {
|
|
||||||
margin-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* page index*/
|
|
||||||
|
|
||||||
.page-index .btn{
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.task-wrap {
|
|
||||||
border: 1px solid var(--primary);
|
|
||||||
padding: 20px 40px 40px 40px;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: inset 0 0 9px 4px rgba(255,255,255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.task {
|
|
||||||
display: flex;
|
|
||||||
border-bottom: 1px solid white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.task a:hover {
|
|
||||||
background-color: rgba(255,255,255,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.task a {
|
|
||||||
display: flex;
|
|
||||||
gap: 15px;
|
|
||||||
width: 100%;
|
|
||||||
padding: 20px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Page projects */
|
|
||||||
|
|
||||||
.page-project h1 {
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-info-wrap{
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* form */
|
|
||||||
|
|
||||||
.form-wrap {
|
|
||||||
max-width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-input {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-input input {
|
|
||||||
height: 25px;
|
|
||||||
border-radius: 5px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
<footer>
|
|
||||||
</footer>
|
|
@ -1,8 +0,0 @@
|
|||||||
<header>
|
|
||||||
<nav class="container">
|
|
||||||
<ul>
|
|
||||||
<li class="current"><a href="/">Home</a></li>
|
|
||||||
<li><a href="/register">Register</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
@ -1,31 +0,0 @@
|
|||||||
{% extends "layouts/base.html" %}
|
|
||||||
{% block content %}
|
|
||||||
<header>
|
|
||||||
<nav class="container">
|
|
||||||
<ul>
|
|
||||||
<li class="current"><a href="/">Home</a></li>
|
|
||||||
<li><a href="/register">Register</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
<main class="container page page-index">
|
|
||||||
<section>
|
|
||||||
<div class="btn">
|
|
||||||
<a href="/addtask">Add new task</a>
|
|
||||||
</div>
|
|
||||||
<div class="tasks-wrap">
|
|
||||||
<h1>Tasks</h1>
|
|
||||||
{% for task in tasks %}
|
|
||||||
<div class="task">
|
|
||||||
<a href="/projects/{{task.id}}">
|
|
||||||
<div>{{task.id}}.</div>
|
|
||||||
<div>{{task.name}}</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
<footer>
|
|
||||||
</footer>
|
|
||||||
{% endblock content %}
|
|
22
taskmanager/templates/index.html
Normal file
22
taskmanager/templates/index.html
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Decentrala</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<section>
|
||||||
|
{% for task in tasks %}
|
||||||
|
<li>{{task}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,15 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<link rel="stylesheet" href="/static/style.css" />
|
|
||||||
<title>Task manager</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
{% include "includes/header.html" %}
|
|
||||||
{% block content %}
|
|
||||||
{% endblock content %}
|
|
||||||
{% include "includes/footer.html" %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,25 +0,0 @@
|
|||||||
{% extends "layouts/base.html" %}
|
|
||||||
{% block content %}
|
|
||||||
<main class="container page page-addtask">
|
|
||||||
<h1>Create new task</h1>
|
|
||||||
<div class="form-wrap">
|
|
||||||
<form action="/addtask" method="POST">
|
|
||||||
<div class="form-input">
|
|
||||||
<label for="taskname" class="label">Task name:</label>
|
|
||||||
<input type="text" name="taskname" id="taskname" required />
|
|
||||||
</div>
|
|
||||||
<div class="form-input">
|
|
||||||
<label for="taskdesc" class="label">Description:</label>
|
|
||||||
<input type="text" name="taskdesc" id="taskdesc" placeholder="optional"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-input">
|
|
||||||
<label for="username" class="label">Username:</label>
|
|
||||||
<input type="text" name="username" id="username" placeholder="optional"/>
|
|
||||||
</div>
|
|
||||||
<div class="btn-wrap">
|
|
||||||
<button class="btn">Submit</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</main>
|
|
||||||
{% endblock content %}
|
|
@ -1,19 +0,0 @@
|
|||||||
{% extends "layouts/base.html" %}
|
|
||||||
{% block content %}
|
|
||||||
<body>
|
|
||||||
<main class="container page page-addtask">
|
|
||||||
<h1>Create new task</h1>
|
|
||||||
<div class="form-wrap">
|
|
||||||
<form action="/projects/{{task.id}}/del" method="POST">
|
|
||||||
<p> Task creator's password <p>
|
|
||||||
<div class="form-input">
|
|
||||||
<label for="password" class="label">password:</label>
|
|
||||||
<input type="password" name="password" id="password" required />
|
|
||||||
</div>
|
|
||||||
<div class="btn-wrap">
|
|
||||||
<button class="btn">DELETE</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</main>
|
|
||||||
{% endblock content %}
|
|
@ -1,21 +0,0 @@
|
|||||||
{% extends "layouts/base.html" %}
|
|
||||||
{% block content %}
|
|
||||||
<main class="container page page-index">
|
|
||||||
<section>
|
|
||||||
<div class="btn">
|
|
||||||
<a href="/addtask">Add new task</a>
|
|
||||||
</div>
|
|
||||||
<div class="tasks-wrap">
|
|
||||||
<h1>Tasks</h1>
|
|
||||||
{% for task in tasks %}
|
|
||||||
<div class="task">
|
|
||||||
<a href="/projects/{{task.id}}">
|
|
||||||
<div>{{task.id}}.</div>
|
|
||||||
<div>{{task.name}}</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{% endblock content %}
|
|
@ -1,36 +0,0 @@
|
|||||||
{% extends "layouts/base.html" %}
|
|
||||||
{% block content %}
|
|
||||||
<main class="container page page-project">
|
|
||||||
<section >
|
|
||||||
<h1>{{task.name}}</h1>
|
|
||||||
<label class="label underline">Description</label>
|
|
||||||
<p>{{task.desc}}</p>
|
|
||||||
</section>
|
|
||||||
<section class="section-task">
|
|
||||||
<div>
|
|
||||||
<label class="label underline">Users added to this task</label>
|
|
||||||
{% for user in users %}
|
|
||||||
<div class="user-info-wrap">
|
|
||||||
<div><b>Username:</b> {{user.username}}</div>
|
|
||||||
<div><b>Contact info:</b> {{user.contact}}</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="label underline"> Add person to task</label>
|
|
||||||
<div class="form-wrap">
|
|
||||||
<form action="/projects/{{task.id}}" method="POST">
|
|
||||||
<div class="form-input">
|
|
||||||
<label for="username" class="label">Username:</label>
|
|
||||||
<input type="text" name="username" id="username" required />
|
|
||||||
</div>
|
|
||||||
<div class="btn-wrap">
|
|
||||||
<button class="btn">Submit</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<p><a href="/projects/{{task.id}}/del">DELETE TASK</a><p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{% endblock content %}
|
|
@ -1,24 +0,0 @@
|
|||||||
{% extends "layouts/base.html" %}
|
|
||||||
{% block content %}
|
|
||||||
<main class="container page page-register">
|
|
||||||
<div class="form-wrap">
|
|
||||||
<form action="/register" method="POST">
|
|
||||||
<div class="form-input">
|
|
||||||
<label for="username">Username:</label>
|
|
||||||
<input type="text" name="username" id="username" required />
|
|
||||||
</div>
|
|
||||||
<div class="form-input">
|
|
||||||
<label for="contact">Contact:</label>
|
|
||||||
<input type="text" name="contact" id="contact" required />
|
|
||||||
</div>
|
|
||||||
<div class="form-input">
|
|
||||||
<label for="password">Password:</label>
|
|
||||||
<input type="password" name="password" placeholder="optional" id="password"/>
|
|
||||||
</div>
|
|
||||||
<div class="btn-wrap">
|
|
||||||
<button class="btn">Submit</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
{% endblock content %}
|
|
@ -1,8 +0,0 @@
|
|||||||
{% extends "layouts/base.html" %}
|
|
||||||
{% block content %}
|
|
||||||
<main class="container page page-project">
|
|
||||||
<section >
|
|
||||||
<p>{{response}}<p>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{% endblock content %}
|
|
20
taskmanager/templates/submit.html
Normal file
20
taskmanager/templates/submit.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Submit</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<form action="/submit" method="POST">
|
||||||
|
<label for="forminput1">forminput1</label>
|
||||||
|
<input type="text" name="forminput1" id="forminput1" required>
|
||||||
|
<label for="forminput2">forminput2</label>
|
||||||
|
<input type="text" name="forminput2" id="forminput2" required>
|
||||||
|
<button> Submit </button>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user