Реализован поиск проектов по имени, восстановление пароля по почте. Начал работать на "задачами" в проектах
This commit is contained in:
parent
25e24462ff
commit
b0837ce46f
17
data/quests.py
Normal file
17
data/quests.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import sqlalchemy
|
||||||
|
from flask_login import UserMixin
|
||||||
|
|
||||||
|
from .db_session import SqlAlchemyBase
|
||||||
|
|
||||||
|
|
||||||
|
class Quests(SqlAlchemyBase, UserMixin):
|
||||||
|
__tablename__ = 'quests'
|
||||||
|
|
||||||
|
id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True)
|
||||||
|
project = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey("projects.id"), nullable=True, default=None)
|
||||||
|
creator = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey("users.id"), nullable=True, default=None)
|
||||||
|
name = sqlalchemy.Column(sqlalchemy.String, nullable=False)
|
||||||
|
description = sqlalchemy.Column(sqlalchemy.String, nullable=True)
|
||||||
|
date_create = sqlalchemy.Column(sqlalchemy.DateTime, default=date.today())
|
||||||
|
deadline = sqlalchemy.Column(sqlalchemy.DateTime, default=date.today())
|
||||||
|
realized = sqlalchemy.Column(sqlalchemy.Boolean, default=False)
|
||||||
7
forms/find_project.py
Normal file
7
forms/find_project.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import SubmitField, StringField
|
||||||
|
|
||||||
|
|
||||||
|
class FindProjectForm(FlaskForm):
|
||||||
|
project = StringField('', default='')
|
||||||
|
submit = SubmitField('Поиск')
|
||||||
14
forms/recovery.py
Normal file
14
forms/recovery.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import EmailField, SubmitField, PasswordField
|
||||||
|
from wtforms.validators import DataRequired
|
||||||
|
|
||||||
|
|
||||||
|
class RecoveryForm(FlaskForm):
|
||||||
|
email = EmailField('Введите почту для восстановления', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Восстановить')
|
||||||
|
|
||||||
|
|
||||||
|
class NewPasswordForm(FlaskForm):
|
||||||
|
password = PasswordField('Пароль', validators=[DataRequired()])
|
||||||
|
repeat_password = PasswordField('Повторите пароль', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Восстановить')
|
||||||
107
main.py
107
main.py
@ -13,10 +13,13 @@ from sqlalchemy import or_
|
|||||||
from functions import check_password, mail, init_db_default, get_projects_data, get_user_data, save_project_logo
|
from functions import check_password, mail, init_db_default, get_projects_data, get_user_data, save_project_logo
|
||||||
from forms.edit_profile import EditProfileForm
|
from forms.edit_profile import EditProfileForm
|
||||||
from forms.login import LoginForm
|
from forms.login import LoginForm
|
||||||
|
from forms.find_project import FindProjectForm
|
||||||
from forms.register import RegisterForm
|
from forms.register import RegisterForm
|
||||||
from forms.new_project import NewProjectForm
|
from forms.new_project import NewProjectForm
|
||||||
|
from forms.recovery import RecoveryForm, NewPasswordForm
|
||||||
|
|
||||||
from data.users import User
|
from data.users import User
|
||||||
|
from data.quests import Quests
|
||||||
from data.files import Files
|
from data.files import Files
|
||||||
from data.projects import Projects
|
from data.projects import Projects
|
||||||
from data.staff_projects import StaffProjects
|
from data.staff_projects import StaffProjects
|
||||||
@ -40,6 +43,66 @@ def base():
|
|||||||
return redirect('/projects')
|
return redirect('/projects')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/project/<int:id_project>')
|
||||||
|
def project(id_project):
|
||||||
|
if current_user.is_authenticated:
|
||||||
|
data_session = db_session.create_session()
|
||||||
|
current_project = data_session.query(Projects).filter(Projects.id == id_project).first()
|
||||||
|
if current_project:
|
||||||
|
staff = data_session.query(StaffProjects).filter(StaffProjects.project == current_project.id).all()
|
||||||
|
if current_user.id == current_project.creator or current_user.id in list(map(lambda x: x.user, staff)):
|
||||||
|
return render_template('project.html', project=current_project, title=current_project.name)
|
||||||
|
else:
|
||||||
|
abort(403)
|
||||||
|
else:
|
||||||
|
abort(404)
|
||||||
|
else:
|
||||||
|
return redirect('/login')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/recovery/confirmation/<token>', methods=['GET', 'POST'])
|
||||||
|
def conf_recovery(token):
|
||||||
|
try:
|
||||||
|
user_email = s.loads(token, max_age=86400)
|
||||||
|
data_session = db_session.create_session()
|
||||||
|
user = data_session.query(User).filter(User.email == user_email).first()
|
||||||
|
if user:
|
||||||
|
form = NewPasswordForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
if form.password.data != form.repeat_password.data:
|
||||||
|
return render_template('recovery.html', title='Восстановление', form=form, recovery=0,
|
||||||
|
message='Пароли не совпадают')
|
||||||
|
status_password = check_password(form.password.data)
|
||||||
|
if status_password != 'OK':
|
||||||
|
return render_template('recovery.html', title='Восстановление', form=form, recovery=0,
|
||||||
|
message=str(status_password))
|
||||||
|
user.set_password(form.password.data)
|
||||||
|
data_session.commit()
|
||||||
|
mail(f'Для аккаунта {user.login}, успешно был обновлен пароль', user.email,
|
||||||
|
'Изменение пароля')
|
||||||
|
return redirect('/login?message=Пароль обновлен')
|
||||||
|
return render_template('recovery.html', title='Восстановление', form=form, recovery=0, message='')
|
||||||
|
else:
|
||||||
|
return redirect('/login?message=Пользователь не найден&danger=True')
|
||||||
|
except SignatureExpired:
|
||||||
|
return redirect('/login?message=Срок действия ссылки истек&danger=True')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/recovery', methods=['GET', 'POST'])
|
||||||
|
def recovery():
|
||||||
|
if not current_user.is_authenticated:
|
||||||
|
form = RecoveryForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
token = s.dumps(form.email.data)
|
||||||
|
link_conf = url_for('conf_recovery', token=token, _external=True)
|
||||||
|
mail(f'Для сбросы пароля пройдите по ссылке: {link_conf}', form.email.data,
|
||||||
|
'Восстановление доступа')
|
||||||
|
return redirect('/login?message=Мы выслали ссылку для сброса вам на почту')
|
||||||
|
return render_template('recovery.html', title='Восстановление пароля', form=form, recovery=True, message='')
|
||||||
|
else:
|
||||||
|
return redirect('/')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/projects/delete/<int:id_project>', methods=['GET', 'POST'])
|
@app.route('/projects/delete/<int:id_project>', methods=['GET', 'POST'])
|
||||||
def delete_project(id_project):
|
def delete_project(id_project):
|
||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated:
|
||||||
@ -70,11 +133,11 @@ def user_view(_login):
|
|||||||
data_session = db_session.create_session()
|
data_session = db_session.create_session()
|
||||||
user = data_session.query(User).filter(User.login == _login).first()
|
user = data_session.query(User).filter(User.login == _login).first()
|
||||||
if user:
|
if user:
|
||||||
projects = data_session.query(Projects).filter(or_(Projects.creator == user.id, Projects.id.in_(
|
current_projects = data_session.query(Projects).filter(or_(Projects.creator == user.id, Projects.id.in_(
|
||||||
list(map(lambda x: x[0], data_session.query(
|
list(map(lambda x: x[0], data_session.query(
|
||||||
StaffProjects.project).filter(
|
StaffProjects.project).filter(
|
||||||
StaffProjects.user == user.id).all()))))).all()
|
StaffProjects.user == user.id).all()))))).all()
|
||||||
resp = list(map(lambda x: get_projects_data(x), projects))
|
resp = list(map(lambda x: get_projects_data(x), current_projects))
|
||||||
return render_template('user_view.html', title=user.name + ' ' + user.surname, user=user,
|
return render_template('user_view.html', title=user.name + ' ' + user.surname, user=user,
|
||||||
list_projects=resp)
|
list_projects=resp)
|
||||||
else:
|
else:
|
||||||
@ -91,21 +154,22 @@ def new_project():
|
|||||||
list_users = list(
|
list_users = list(
|
||||||
map(lambda x: get_user_data(x), data_session.query(User).filter(User.id != current_user.id).all()))
|
map(lambda x: get_user_data(x), data_session.query(User).filter(User.id != current_user.id).all()))
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
project = Projects(
|
currnet_project = Projects(
|
||||||
name=form.name.data,
|
name=form.name.data,
|
||||||
description=form.description.data,
|
description=form.description.data,
|
||||||
date_create=datetime.datetime.now(),
|
date_create=datetime.datetime.now(),
|
||||||
creator=current_user.id
|
creator=current_user.id
|
||||||
)
|
)
|
||||||
project.photo = save_project_logo(form.logo.data) if form.logo.data else 'static/images/none_project.png'
|
currnet_project.photo = save_project_logo(
|
||||||
data_session.add(project)
|
form.logo.data) if form.logo.data else 'static/images/none_project.png'
|
||||||
|
data_session.add(currnet_project)
|
||||||
data_session.flush()
|
data_session.flush()
|
||||||
data_session.refresh(project)
|
data_session.refresh(currnet_project)
|
||||||
for i in list_users:
|
for i in list_users:
|
||||||
if request.form.getlist(f"choose_{i['login']}") and i['id'] != current_user.id:
|
if request.form.getlist(f"choose_{i['login']}") and i['id'] != current_user.id:
|
||||||
new_staffer = StaffProjects(
|
new_staffer = StaffProjects(
|
||||||
user=i['id'],
|
user=i['id'],
|
||||||
project=project.id,
|
project=currnet_project.id,
|
||||||
role='user',
|
role='user',
|
||||||
permission=3
|
permission=3
|
||||||
)
|
)
|
||||||
@ -120,18 +184,29 @@ def new_project():
|
|||||||
|
|
||||||
|
|
||||||
@app.route('/projects', methods=['GET', 'POST'])
|
@app.route('/projects', methods=['GET', 'POST'])
|
||||||
def project():
|
def projects():
|
||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated:
|
||||||
|
find = False
|
||||||
|
form = FindProjectForm()
|
||||||
data_session = db_session.create_session()
|
data_session = db_session.create_session()
|
||||||
resp = []
|
resp = []
|
||||||
if request.method == 'POST':
|
current_projects = \
|
||||||
pass
|
data_session.query(Projects).filter(or_(Projects.creator == current_user.id,
|
||||||
else:
|
Projects.id.in_(
|
||||||
projects = data_session.query(Projects).filter(or_(Projects.creator == current_user.id, current_user.id in
|
list(map(lambda x: x[0],
|
||||||
data_session.query(StaffProjects.project).filter(
|
data_session.query(
|
||||||
StaffProjects.user == current_user.id).all())).all()
|
StaffProjects.project).filter(
|
||||||
resp = list(map(lambda x: get_projects_data(x), projects))
|
StaffProjects.user
|
||||||
return render_template('projects.html', title='Проекты', list_projects=resp)
|
== current_user.id).all()))))).all()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
new_resp = []
|
||||||
|
for i in range(len(current_projects)):
|
||||||
|
if str(form.project.data).lower().strip() in str(current_projects[i].name).lower().strip():
|
||||||
|
new_resp.append(current_projects[i])
|
||||||
|
current_projects = new_resp
|
||||||
|
find = True
|
||||||
|
resp = list(map(lambda x: get_projects_data(x), current_projects))
|
||||||
|
return render_template('projects.html', title='Проекты', list_projects=resp, form=form, find=find)
|
||||||
else:
|
else:
|
||||||
return redirect('/login')
|
return redirect('/login')
|
||||||
|
|
||||||
|
|||||||
@ -93,3 +93,13 @@
|
|||||||
.box {
|
.box {
|
||||||
margin-left: 9vw;
|
margin-left: 9vw;
|
||||||
}
|
}
|
||||||
|
.recovery_button {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 1.5vw;
|
||||||
|
transition: color 0.5s ease-in, border-bottom 0.5s ease-in;
|
||||||
|
}
|
||||||
|
.recovery_button:hover {
|
||||||
|
color: #694a2d;
|
||||||
|
border-bottom: 3px solid #f3c79e;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
28
static/css/project.css
Normal file
28
static/css/project.css
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
.projects_page {
|
||||||
|
height: 120vw;
|
||||||
|
background-color: #dcb495;
|
||||||
|
}
|
||||||
|
.project_header {
|
||||||
|
height: 25vw;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
.project_logo {
|
||||||
|
margin-top: 30px;
|
||||||
|
width: 15vw;
|
||||||
|
height: 15vw;
|
||||||
|
border: 0.2vw solid #ffffff;
|
||||||
|
border-radius: 2vw;
|
||||||
|
}
|
||||||
|
.brand_block {
|
||||||
|
height: 25vw;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.name_project {
|
||||||
|
font-size: 3vw;
|
||||||
|
}
|
||||||
@ -234,7 +234,7 @@
|
|||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
color: #000000 !important;
|
color: #000000 !important;
|
||||||
}
|
}
|
||||||
.new_project_button {
|
.new_project_button, .find_project_button {
|
||||||
width: 13vw;
|
width: 13vw;
|
||||||
height: 5vw;
|
height: 5vw;
|
||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
@ -242,7 +242,7 @@
|
|||||||
border-radius: 3vw;
|
border-radius: 3vw;
|
||||||
margin-left: 2vw;
|
margin-left: 2vw;
|
||||||
}
|
}
|
||||||
.new_project_button_text {
|
.new_project_button_text, .find_project_button_text {
|
||||||
width: 13vw;
|
width: 13vw;
|
||||||
height: 5vw;
|
height: 5vw;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -252,11 +252,11 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
.new_project_button_link {
|
.new_project_button_lin, find_project_button_linkk {
|
||||||
width: 13vw;
|
width: 13vw;
|
||||||
height: 5vw;
|
height: 5vw;
|
||||||
}
|
}
|
||||||
.new_project_button_link:hover {
|
.new_project_button_link:hover, .find_project_button_linkk:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #000000;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|||||||
61
static/css/recovery.css
Normal file
61
static/css/recovery.css
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
.recovery_page {
|
||||||
|
height: 60vw;
|
||||||
|
background-color: #dcb495;
|
||||||
|
}
|
||||||
|
.recovery {
|
||||||
|
width: 100%;
|
||||||
|
height: 60vw;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.header_title {
|
||||||
|
text-align: center;
|
||||||
|
color: #000000;
|
||||||
|
font-size: 3.5vw;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.recovery_form {
|
||||||
|
margin-top: 55px;
|
||||||
|
width: 70%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.data_block {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-end;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
.form_data {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: 2%;
|
||||||
|
margin-left: 2%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.form-label {
|
||||||
|
font-size: 1.3vw;
|
||||||
|
}
|
||||||
|
.input_data {
|
||||||
|
color: #000000;
|
||||||
|
border: 0.1vw solid #595008;
|
||||||
|
height: 4.7vw;
|
||||||
|
width: 30vw;
|
||||||
|
background-color: #dbc3af;
|
||||||
|
border-radius: 5vw;
|
||||||
|
font-size: 1.3vw;
|
||||||
|
}
|
||||||
|
.recovery_button {
|
||||||
|
background-color: #000000;
|
||||||
|
color: #ffffff;
|
||||||
|
min-width: 20vw !important;
|
||||||
|
height: 5vw;
|
||||||
|
border-radius: 5vw;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 1.5vw;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
@ -2,14 +2,14 @@
|
|||||||
<html lang="ru">
|
<html lang="ru">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="stylesheet" href="../static/css/base.css" />
|
<link rel="stylesheet" href="../../static/css/base.css" />
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
|
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
|
||||||
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
|
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
/>
|
/>
|
||||||
<link rel="icon" href="../static/images/logo_b.ico" type="image/x-icon" />
|
<link rel="icon" href="../../static/images/logo_b.ico" type="image/x-icon" />
|
||||||
<title>{{title}}</title>
|
<title>{{title}}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -38,7 +38,7 @@
|
|||||||
<nav class="navbar" id="navbar">
|
<nav class="navbar" id="navbar">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="/">
|
<a class="navbar-brand" href="/">
|
||||||
<img src="../static/images/logo_b.png" class="nav_logo" />
|
<img src="../../static/images/logo_b.png" class="nav_logo" />
|
||||||
</a>
|
</a>
|
||||||
<a class="auth_button" href="/login">Авторизация</a>
|
<a class="auth_button" href="/login">Авторизация</a>
|
||||||
</div>
|
</div>
|
||||||
@ -49,7 +49,7 @@
|
|||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class="footer_block">
|
<div class="footer_block">
|
||||||
<a href="/#header_block"
|
<a href="/#header_block"
|
||||||
><img class="footer_logo" src="../static/images/logo_w.png"
|
><img class="footer_logo" src="../../static/images/logo_w.png"
|
||||||
/></a>
|
/></a>
|
||||||
<strong class="footer_rights">© All rights reserved</strong>
|
<strong class="footer_rights">© All rights reserved</strong>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -26,6 +26,9 @@
|
|||||||
<a class="input_button register_button" type="submit" href="/register">
|
<a class="input_button register_button" type="submit" href="/register">
|
||||||
<div class="register"><strong>Регистрация</strong></div>
|
<div class="register"><strong>Регистрация</strong></div>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="recovery_button" type="submit" href="/recovery">
|
||||||
|
<div class="recovery"><strong>Забыли пароль?</strong></div>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
|
|||||||
28
templates/project.html
Normal file
28
templates/project.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<link rel="stylesheet" href="../static/css/project.css"/>
|
||||||
|
{% extends "base.html" %} {% block content %}
|
||||||
|
<div class="projects_page">
|
||||||
|
<div class="project_header">
|
||||||
|
<div class="edit_block">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="brand_block">
|
||||||
|
<img class="project_logo" src="../{{project.photo}}"/>
|
||||||
|
<p class="name_project">{{ project.name }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="notification_block">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="body_block">
|
||||||
|
<div class="staff_block">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="task_block">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="files_block">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@ -8,8 +8,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="find_block">
|
<div class="find_block">
|
||||||
<form action="" method="post" class="form_project_block">
|
<form action="" method="post" class="form_project_block">
|
||||||
<input class="find_input_text" type="text" placeholder="Имя проекта" name="find_text">
|
{{ form.hidden_tag() }}
|
||||||
<button class="find_input_button">Поиск</button>
|
{{ form.project(class="find_input_text", type="text", placeholder='Имя проекта') }}
|
||||||
|
{% for error in form.project.errors %}
|
||||||
|
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
{{ form.submit(type="submit", class="find_input_button") }}
|
||||||
|
{% if find == 1 %}
|
||||||
|
<div class="find_project_button">
|
||||||
|
<a class="find_project_button_link" href="/projects">
|
||||||
|
<p class="find_project_button_text">Сброс</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<div class="new_project_button">
|
<div class="new_project_button">
|
||||||
<a class="new_project_button_link" href="/projects/new">
|
<a class="new_project_button_link" href="/projects/new">
|
||||||
<p class="new_project_button_text">Создать</p>
|
<p class="new_project_button_text">Создать</p>
|
||||||
@ -35,7 +46,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div id="panelsStayOpen-collapse{{ project.id }}" class="accordion-collapse collapse project_description_block"
|
<div id="panelsStayOpen-collapse{{ project.id }}"
|
||||||
|
class="accordion-collapse collapse project_description_block"
|
||||||
aria-labelledby="panelsStayOpen-heading{{ project.id }}">
|
aria-labelledby="panelsStayOpen-heading{{ project.id }}">
|
||||||
<div class="accordion-body project_description">
|
<div class="accordion-body project_description">
|
||||||
<div class="collaborator_block">
|
<div class="collaborator_block">
|
||||||
@ -60,7 +72,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="open_project_block">
|
<div class="open_project_block">
|
||||||
<div class="open_button">
|
<div class="open_button">
|
||||||
<a class="open_button_link" href="/projects/{{ project.id }}">
|
<a class="open_button_link" href="/project/{{ project.id }}">
|
||||||
<p class="open_button_text">Открыть</p>
|
<p class="open_button_text">Открыть</p>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
52
templates/recovery.html
Normal file
52
templates/recovery.html
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<link rel="stylesheet" href="../../static/css/recovery.css"/>
|
||||||
|
{% extends "base.html" %} {% block content %}
|
||||||
|
<div class="recovery_page">
|
||||||
|
<div class="recovery">
|
||||||
|
{% if recovery == 1 %}
|
||||||
|
<h1 class="header_title">Восстановление пароля</h1>
|
||||||
|
<form action="" method="post" class="recovery_form">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
<div class="data_block">
|
||||||
|
<div class="form_data">
|
||||||
|
<label class="form-label">{{ form.email.label }}</label>
|
||||||
|
{{ form.email(class="input_data", type="email", placeholder='example@mail.ex') }}
|
||||||
|
{% for error in form.email.errors %}
|
||||||
|
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{{ form.submit(type="submit", class="recovery_button") }}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<h1 class="header_title">Восстановление пароля</h1>
|
||||||
|
<form action="" method="post" class="recovery_form">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
<div class="data_block">
|
||||||
|
<div class="form_data">
|
||||||
|
<label class="form-label">{{ form.password.label }}</label>
|
||||||
|
{{ form.password(class="input_data", type="password", placeholder='good_password') }}
|
||||||
|
{% for error in form.password.errors %}
|
||||||
|
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="form_data">
|
||||||
|
<label class="form-label">{{ form.repeat_password.label }}</label>
|
||||||
|
{{ form.repeat_password(class="input_data", type="password", placeholder='good_password') }}
|
||||||
|
{% for error in form.repeat_password.errors %}
|
||||||
|
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{{ form.submit(type="submit", class="recovery_button") }}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
<div class="message_block">
|
||||||
|
{% if message != '' %}
|
||||||
|
<div class="alert alert-danger message" role="alert">
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
Loading…
x
Reference in New Issue
Block a user