Compare commits

...

37 Commits

Author SHA1 Message Date
cda12028c1
make Makefile
Every project should have a Makefile ;)
2024-06-10 20:23:57 +02:00
7ea0a7ddb7
fix typo 2024-06-10 20:23:08 +02:00
a2ec1aeebd
fix typo 2024-02-14 00:25:59 +01:00
004b1a1c37 Merge branch 'fix/relative-path-and-docs' 2024-02-14 00:05:04 +01:00
48e98cbfc3
add more user input santitation 2024-02-14 00:04:00 +01:00
e632ca23a5 Merge pull request 'Changed to relative path and updated the documentation' (#6) from fix/relative-path-and-docs into master
Reviewed-on: #6
2024-01-28 19:50:48 +00:00
coja
6739e997bc Changed to relative path and updated the documentation 2024-01-21 13:16:44 +01:00
33cd9c202e Merge pull request 'Prebaceni *.html fajlovi za stranice u pages folder' (#5) from svitvojimilioni/taskmanager:better-templates into master
Reviewed-on: #5
2024-01-19 16:27:43 +00:00
efcab93460 Prebaceni *.html fajlovi za stranice u pages folder 2024-01-19 11:23:04 -05:00
b4344d31a8 Merge pull request 'Izmenjen templates folder tako da bude modularniji' (#4) from svitvojimilioni/taskmanager:better-templates into master
Reviewed-on: #4
2024-01-19 16:17:37 +00:00
2d4b88bc7a Vracen path za config na staro 2024-01-19 11:15:58 -05:00
2721d2a524 Izmenjen templates folder tako da bude modularniji
- dodati folderi layouts, includes, pages
- dodate base layout koji koriste ostale stranice
- dodat header.html i footer.html i includes koji koriste ostale stranice
- sav html refaktorisan tako da koristi base.html layout i header i footer iz includes
2024-01-19 10:53:12 -05:00
13d1e1674d
fix taskuser delition after removing tasks and repsonse.html 2024-01-19 00:07:17 +01:00
44b7228982
fix errors when empty optinal values 2024-01-18 23:34:01 +01:00
673c04af19
add response.html and deleting user assignment to deleted tasks 2024-01-18 23:15:22 +01:00
4911842d3f
fix check for already added users to task 2024-01-18 22:53:35 +01:00
d71cde1171
check if user already added to task 2024-01-18 22:41:26 +01:00
e615f774ad
add user input sanitation 2024-01-18 15:00:13 +01:00
ed38156e77
add sql db and config file to install scripts for saving 2024-01-18 14:26:27 +01:00
4b82097fd9
update debian control info 2024-01-18 14:05:50 +01:00
d0d9529e41
add task deleting 2024-01-18 14:04:00 +01:00
a1c41349bd
update README install instructions and add var to .gitignore 2024-01-18 12:37:07 +01:00
066bac721b
add password to user registration and remove file copies from build dir 2024-01-18 12:32:09 +01:00
5964ef4963 Merge pull request 'front/app-design-init' (#3) from front/app-design-init into master
Reviewed-on: #3
2024-01-18 10:33:56 +00:00
coja
8acbb02a2f README update 2024-01-18 02:04:32 +01:00
coja
2c4c9a9802 [FE] Design init 2024-01-18 01:58:03 +01:00
8aa2675240
add deb files to gitignore and change permissions of install scripts 2024-01-17 15:35:47 +01:00
e5c424fff8
fix install script path in makefile 2024-01-17 15:13:56 +01:00
b3f257b7f4
fix build files 2024-01-17 15:12:42 +01:00
9f2817fcf8
implement addtask and addusers to a task features 2024-01-16 14:44:06 +01:00
d81ff0592e
add project page 2024-01-16 13:44:51 +01:00
52866f9c84
fix init_db file and run.py 2024-01-16 13:28:11 +01:00
d9a73a6fd9
change more filenames and strings to say taskmanager instead of flaskapp 2024-01-16 13:24:45 +01:00
7aa57af9ef
add register html 2024-01-15 20:37:45 +01:00
5166777e40 Text color 2024-01-15 20:35:29 +01:00
33c71cf93d Merge pull request 'FE tst commit' (#1) from fe_init into master
Reviewed-on: #1
2024-01-15 19:18:55 +00:00
dd142693d9
add models and index route 2024-01-15 20:15:44 +01:00
31 changed files with 640 additions and 83 deletions

5
.gitignore vendored
View File

@ -1,4 +1,3 @@
# 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
*.db
@ -24,7 +23,7 @@ dist/
downloads/
eggs/
.eggs/
lib/
#lib/
lib64/
parts/
sdist/
@ -234,3 +233,5 @@ tags
# End of https://www.toptal.com/developers/gitignore/api/python,flask,vim,visualstudiocode
# deb files
*.deb

View File

@ -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.
flaskapp
taskmanager
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.

19
Makefile Normal file
View File

@ -0,0 +1,19 @@
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

View File

@ -1,4 +1,39 @@
# flaskapp
# Task Manager
Web app
Interactive TODO list web application
# 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
```

View File

@ -7,8 +7,12 @@ man: man/taskmanager.1.md
deb: man ../requirments.txt ../run.py ../taskmanager ../LICENSE
cp -r ../taskmanager/* taskmanager/var/taskmanager/taskmanager/
cp ../run.py taskmanager/var/taskmanager/
cp ../init_db.py taskmanager/var/taskmanager/
cp ../LICENSE taskmanager/var/taskmanager/
chmod -w taskmanager/DEBIAN/*
chmod +w taskmanager/DEBIAN/control
dpkg-deb --build taskmanager
chmod +w taskmanager/DEBIAN/*
clean:
rm -f taskmanager.deb
rm -f man/taskmanager.1

View File

@ -1,15 +1,15 @@
% FLASKAPP(1) taskmanager 1.0.0
% TASKMANAGER(1) taskmanager 1.0.0
% Decentrala
% Jun 2023
# NAME
taskmanager - Web app
taskmanager - Interactive TODO list Web app
# SYNOPSIS
**python3 run.py**
# DESCRIPTION
Web app
Interactive TODO list Web app
# AUTHORS
Decentrala

View File

@ -7,5 +7,5 @@ Installed-Size: 2000
Depends: gunicorn, python3-flask-sqlalchemy
Homepage: https://gitea.dmz.rs/Decentrala/taskmanager
Maintainer: Decentrala <dmz@dmz.rs>
Description: Web app
Version: 1.0.0
Description: Interactive TODO list Web app
Version: 1.0.10

View File

@ -1,3 +1,12 @@
#!/bin/sh
/usr/bin/systemctl enable taskmanager.service
/var/taskmanager/init_db.py
/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

View File

@ -0,0 +1,6 @@
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

View File

@ -1,3 +1,9 @@
#!/bin/sh
/sbin/service taskmanager stop
/usr/bin/systemdctl disable taskmanager.service
/usr/bin/systemctl 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

View File

@ -0,0 +1,12 @@
[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

View File

@ -1,9 +1,10 @@
#!/usr/bin/env python3
from flaskapp import db
from taskmanager import db, app
print('[i] Trying to create databse...')
try:
db.create_all()
with app.app_context():
db.create_all()
print('[+] Success you can proceed with deployment!')
except:
print('[-] Creating db failed :/')

3
run.py
View File

@ -1,4 +1,5 @@
from flaskapp import app
#!/usr/bin/env python3
from taskmanager import app
if __name__ == '__main__':
app.run(debug=False)

2
taskmanager/config.ini Normal file
View File

@ -0,0 +1,2 @@
[credentials]
ADMINPASS = defaultpassword

View File

@ -0,0 +1,17 @@
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

View File

@ -1,7 +1,19 @@
from taskmanager import db
class Table(db.Model):
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
variable1 = db.Column(db.Integer, nullable=False)
variable2 = db.Column(db.Integer, nullable=False)
name = db.Column(db.String, nullable=False)
desc = db.Column(db.String, nullable=True)
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)

View File

@ -1,34 +1,184 @@
import configparser
import os
from flask import render_template, request, redirect
from taskmanager import app, db
from taskmanager.functions 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'])
def index():
if request.method == 'GET':
localvariable1 = "Placeholder1"
localvariable1 = "Placeholder2"
try:
return render_template('index.html', pagevariable1 = localvariable1, pagevariable2 = localvariable2 )
except:
return 'Error retriving page'
else:
return 'HTTP request method not recogniezed'
tasks = Task.query.all()
return render_template('pages/index.html', tasks = tasks)
@app.route('/submit', methods=['POST', 'GET'])
def submit():
@app.route('/addtask', methods=['GET','POST'])
def addtask():
if request.method == 'GET':
return render_template('submit.html')
return render_template('pages/addtask.html')
elif request.method == 'POST':
userinput1 = request.form['forminput1']
userinput2 = request.form['forminput2']
sqlrow = Table(variable1 = int(userinput1), variable2 = int(userinput2))
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:
db.session.add(sqlrow)
db.session.add(sqladdtask)
db.session.commit()
return 'Row added'
return render_template('pages/response.html', response = 'Task added')
except:
return 'Adding row to table failed'
return render_template('pages/response.html', response = 'Adding task failed')
@app.route('/register', methods=['POST', 'GET'])
def register():
if request.method == 'GET':
return render_template('pages/register.html')
elif request.method == 'POST':
username = request.form['username']
contact = request.form['contact']
password = request.form['password']
# 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:
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')
else:
return 'HTTP request method not recogniezed'
return render_template('pages/response.html', response = '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')

View File

@ -1,24 +1,36 @@
:root {
--border-radus: 1rem;
--background: #000000;
--header-background: #FFFFFF;
--background: #383840;
--header-background: #212121;
--header-height: 3rem;
--input-bar-height: 3rem;
--white: #FFF;
--primary: #d2d2d2;
--font-primary: #d2d2d2;
color: #FFF;
}
body{
background: var(--background);
font-family: sans-serif;
* {
margin: 0;
padding: 0;
}
a, a:visited {
color: var(--font-primary);
}
body {
background: var(--background);
font-family: sans-serif;
}
main {
display: flex;
flex-direction: column;
align-self: flex-start;
gap: 1rem;
margin: 0 auto;
padding: 30px 0;
height: calc(100vh - var(--header-height) - var(--input-bar-height));
box-sizing: border-box;
}
@ -35,6 +47,12 @@ header {
box-sizing: border-box;
}
header ul {
display: flex;
list-style-type: none;
gap:15px
}
footer {
width: 100vw;
height: var(--input-bar-height);
@ -46,3 +64,120 @@ footer {
box-sizing: border-box;
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;
}

View File

@ -0,0 +1,2 @@
<footer>
</footer>

View File

@ -0,0 +1,8 @@
<header>
<nav class="container">
<ul>
<li class="current"><a href="/">Home</a></li>
<li><a href="/register">Register</a></li>
</ul>
</nav>
</header>

View File

@ -0,0 +1,31 @@
{% 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 %}

View File

@ -1,22 +0,0 @@
<!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>

View File

@ -0,0 +1,15 @@
<!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>

View File

@ -0,0 +1,25 @@
{% 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 %}

View File

@ -0,0 +1,19 @@
{% 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 %}

View File

@ -0,0 +1,21 @@
{% 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 %}

View File

@ -0,0 +1,36 @@
{% 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 %}

View File

@ -0,0 +1,24 @@
{% 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 %}

View File

@ -0,0 +1,8 @@
{% extends "layouts/base.html" %}
{% block content %}
<main class="container page page-project">
<section >
<p>{{response}}<p>
</section>
</main>
{% endblock content %}

View File

@ -1,20 +0,0 @@
<!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>