Создана страница добавления проектов, проекты отображаются в списке проектов, добавлена функция удаления проекта
This commit is contained in:
parent
709d2f971d
commit
22da6ee3ef
@ -13,7 +13,7 @@ class Projects(SqlAlchemyBase, UserMixin):
|
|||||||
name = sqlalchemy.Column(sqlalchemy.String, nullable=False)
|
name = sqlalchemy.Column(sqlalchemy.String, nullable=False)
|
||||||
description = sqlalchemy.Column(sqlalchemy.String, nullable=True)
|
description = sqlalchemy.Column(sqlalchemy.String, nullable=True)
|
||||||
photo = sqlalchemy.Column(sqlalchemy.Text)
|
photo = sqlalchemy.Column(sqlalchemy.Text)
|
||||||
date_create = sqlalchemy.Column(sqlalchemy.Date,
|
date_create = sqlalchemy.Column(sqlalchemy.DateTime,
|
||||||
default=date.today())
|
default=date.today())
|
||||||
creator = sqlalchemy.Column(sqlalchemy.Integer,
|
creator = sqlalchemy.Column(sqlalchemy.Integer,
|
||||||
sqlalchemy.ForeignKey("users.id"), nullable=True, default=None)
|
sqlalchemy.ForeignKey("users.id"), nullable=True, default=None)
|
||||||
|
|||||||
@ -7,4 +7,4 @@ class NewProjectForm(FlaskForm):
|
|||||||
name = StringField('Название', validators=[DataRequired()])
|
name = StringField('Название', validators=[DataRequired()])
|
||||||
description = TextAreaField('Описание')
|
description = TextAreaField('Описание')
|
||||||
logo = FileField('Логотип')
|
logo = FileField('Логотип')
|
||||||
submit = SubmitField('Регистрация')
|
submit = SubmitField('Создать')
|
||||||
|
|||||||
13
functions.py
13
functions.py
@ -4,6 +4,7 @@ from data.roles import Roles
|
|||||||
from data.users import User
|
from data.users import User
|
||||||
from data.staff_projects import StaffProjects
|
from data.staff_projects import StaffProjects
|
||||||
from data import db_session
|
from data import db_session
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
def check_password(password=''):
|
def check_password(password=''):
|
||||||
@ -56,6 +57,7 @@ def init_db_default():
|
|||||||
|
|
||||||
def get_user_data(user):
|
def get_user_data(user):
|
||||||
resp = {
|
resp = {
|
||||||
|
'id': user.id,
|
||||||
'name': user.name,
|
'name': user.name,
|
||||||
'surname': user.surname,
|
'surname': user.surname,
|
||||||
'login': user.login,
|
'login': user.login,
|
||||||
@ -68,12 +70,21 @@ def get_user_data(user):
|
|||||||
|
|
||||||
def get_projects_data(project):
|
def get_projects_data(project):
|
||||||
data_session = db_session.create_session()
|
data_session = db_session.create_session()
|
||||||
|
staff = data_session.query(StaffProjects.user).filter(StaffProjects.project == project.id).all()
|
||||||
resp = {
|
resp = {
|
||||||
'id': project.id,
|
'id': project.id,
|
||||||
'name': project.name,
|
'name': project.name,
|
||||||
'logo': project.photo,
|
'logo': project.photo,
|
||||||
'description': project.description,
|
'description': project.description,
|
||||||
'staff': list(map(lambda x: get_user_data(x), data_session.query(User).filter(
|
'staff': list(map(lambda x: get_user_data(x), data_session.query(User).filter(
|
||||||
User.id.in_(*data_session.query(StaffProjects.user).filter(StaffProjects.id == project.id).all())).all()))
|
User.id.in_(list(map(lambda x: x[0], staff)))).all())) if staff else []
|
||||||
}
|
}
|
||||||
|
resp['staff'].insert(0, get_user_data(data_session.query(User).filter(User.id == project.creator).first()))
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
def save_project_logo(photo):
|
||||||
|
filename = f'static/app_files/project_logo/{uuid.uuid4()}.png'
|
||||||
|
with open(filename, 'wb') as f:
|
||||||
|
photo.save(f)
|
||||||
|
return filename
|
||||||
|
|||||||
66
main.py
66
main.py
@ -1,15 +1,16 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
import pprint
|
|
||||||
|
|
||||||
from flask import Flask, render_template, request, url_for
|
from flask import Flask, render_template, request, url_for
|
||||||
from flask_login import login_user, current_user, LoginManager, logout_user, login_required
|
from flask_login import login_user, current_user, LoginManager, logout_user, login_required
|
||||||
|
from flask_wtf import CSRFProtect
|
||||||
|
from flask_restful import abort
|
||||||
from werkzeug.datastructures import CombinedMultiDict
|
from werkzeug.datastructures import CombinedMultiDict
|
||||||
from werkzeug.utils import redirect
|
from werkzeug.utils import redirect
|
||||||
from itsdangerous import URLSafeTimedSerializer, SignatureExpired
|
from itsdangerous import URLSafeTimedSerializer, SignatureExpired
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
|
|
||||||
from functions import check_password, mail, init_db_default, get_projects_data
|
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.register import RegisterForm
|
from forms.register import RegisterForm
|
||||||
@ -25,6 +26,7 @@ from data import db_session
|
|||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
key = 'test_secret_key'
|
key = 'test_secret_key'
|
||||||
app.config['SECRET_KEY'] = key
|
app.config['SECRET_KEY'] = key
|
||||||
|
csrf = CSRFProtect(app)
|
||||||
s = URLSafeTimedSerializer(key)
|
s = URLSafeTimedSerializer(key)
|
||||||
login_manager = LoginManager()
|
login_manager = LoginManager()
|
||||||
login_manager.init_app(app)
|
login_manager.init_app(app)
|
||||||
@ -38,13 +40,62 @@ def base():
|
|||||||
return redirect('/projects')
|
return redirect('/projects')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/projects/delete/<int:id_project>', methods=['GET', 'POST'])
|
||||||
|
def delete_project(id_project):
|
||||||
|
if current_user.is_authenticated:
|
||||||
|
data_session = db_session.create_session()
|
||||||
|
project_del = data_session.query(Projects).filter(Projects.id == id_project).first()
|
||||||
|
if project_del:
|
||||||
|
if project_del.creator == current_user.id:
|
||||||
|
staff = data_session.query(StaffProjects).filter(StaffProjects.project == id_project).all()
|
||||||
|
for i in staff:
|
||||||
|
data_session.delete(i)
|
||||||
|
if 'none_project' not in project_del.photo:
|
||||||
|
os.remove(project_del.photo)
|
||||||
|
data_session.delete(project_del)
|
||||||
|
data_session.commit()
|
||||||
|
data_session.close()
|
||||||
|
return redirect('/projects')
|
||||||
|
else:
|
||||||
|
abort(403)
|
||||||
|
else:
|
||||||
|
abort(404)
|
||||||
|
else:
|
||||||
|
return redirect('/login')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/projects/new', methods=['GET', 'POST'])
|
@app.route('/projects/new', methods=['GET', 'POST'])
|
||||||
def new_project():
|
def new_project():
|
||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated:
|
||||||
form = NewProjectForm()
|
form = NewProjectForm()
|
||||||
|
data_session = db_session.create_session()
|
||||||
|
list_users = list(
|
||||||
|
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():
|
||||||
pass
|
project = Projects(
|
||||||
return render_template('new_project.html', title='Новый проект', form=form)
|
name=form.name.data,
|
||||||
|
description=form.description.data,
|
||||||
|
date_create=datetime.datetime.now(),
|
||||||
|
creator=current_user.id
|
||||||
|
)
|
||||||
|
project.photo = save_project_logo(form.logo.data) if form.logo.data else 'static/images/none_project.png'
|
||||||
|
data_session.add(project)
|
||||||
|
data_session.flush()
|
||||||
|
data_session.refresh(project)
|
||||||
|
for i in list_users:
|
||||||
|
if request.form.getlist(f"choose_{i['login']}") and i['id'] != current_user.id:
|
||||||
|
new_staffer = StaffProjects(
|
||||||
|
user=i['id'],
|
||||||
|
project=project.id,
|
||||||
|
role='user',
|
||||||
|
permission=3
|
||||||
|
)
|
||||||
|
data_session.add(new_staffer)
|
||||||
|
data_session.commit()
|
||||||
|
data_session.close()
|
||||||
|
return redirect('/projects')
|
||||||
|
data_session.close()
|
||||||
|
return render_template('new_project.html', title='Новый проект', form=form, list_users=list_users)
|
||||||
else:
|
else:
|
||||||
return redirect('/login')
|
return redirect('/login')
|
||||||
|
|
||||||
@ -218,7 +269,12 @@ def confirmation(token):
|
|||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def page_not_found(error):
|
def page_not_found(error):
|
||||||
return render_template('page404.html', title='Страница не найдена')
|
return render_template('page_error.html', title='Страница не найдена', error='404', message='Страница не найдена')
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(403)
|
||||||
|
def page_not_found(error):
|
||||||
|
return render_template('page_error.html', title='Ошибка доступа', error='403', message='Доступ сюда запрещен')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
0
static/app_files/project_logo/заглушка
Normal file
0
static/app_files/project_logo/заглушка
Normal file
@ -26,13 +26,108 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.input_button {
|
.input_button {
|
||||||
width: 10vw;
|
width: 35vw;
|
||||||
height: 5vw;
|
height: 5vw;
|
||||||
border-radius: 5vw;
|
border-radius: 5vw;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.form_label {
|
.form_label {
|
||||||
|
margin-top: 10px;
|
||||||
font-size: 1.3vw;
|
font-size: 1.3vw;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.description {
|
||||||
|
border-radius: 2vw !important;
|
||||||
|
width: 50vw;
|
||||||
|
}
|
||||||
|
.padding_data {
|
||||||
|
padding-top: 1vw;
|
||||||
|
padding-left: 1vw;
|
||||||
|
}
|
||||||
|
.label_data {
|
||||||
|
padding-left: 0.8vw;
|
||||||
|
width: 50vw;
|
||||||
|
}
|
||||||
|
.project_button {
|
||||||
|
margin-top: 15px;
|
||||||
|
width: 35vw;
|
||||||
|
height: 5vw;
|
||||||
|
background-color: #000000;
|
||||||
|
color: #ffffff;
|
||||||
|
border-radius: 5vw;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 1.5vw;
|
||||||
|
}
|
||||||
|
.collaborator_block {
|
||||||
|
width: 30%;
|
||||||
|
height: 20vw;
|
||||||
|
background-color: #EDCBB0;
|
||||||
|
border-radius: 2vw;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.user {
|
||||||
|
width: 30vw;
|
||||||
|
height: 3.5vw;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 2px solid #9E795A;
|
||||||
|
border-radius: 3vw;
|
||||||
|
margin-top: 5px;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.user_logo {
|
||||||
|
margin-left: 3px;
|
||||||
|
width: 3vw;
|
||||||
|
height: 3vw;
|
||||||
|
border-radius: 5vw;
|
||||||
|
background-color: #000000;
|
||||||
|
}
|
||||||
|
.user_names {
|
||||||
|
margin-left: 9px;
|
||||||
|
margin-top: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.name_form_block {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.data_form_block {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
.buttons_form_block {
|
||||||
|
width: 45%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.staff_form_block, .collaborator_block {
|
||||||
|
margin-top: 10px;
|
||||||
|
width: 60%;
|
||||||
|
height: 20vw;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.staff_block {
|
||||||
|
margin: 5%;
|
||||||
|
width: 90%;
|
||||||
|
height: 20vw;
|
||||||
|
}
|
||||||
|
.choose_user {
|
||||||
|
align-self: flex-end;
|
||||||
|
margin-bottom: 1.1vw;
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
|
.user_data {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
.navbar {
|
.navbar {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
.page_404 {
|
.page_error {
|
||||||
height: 55vw;
|
height: 55vw;
|
||||||
background-color: #dcb495;
|
background-color: #dcb495;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -150,7 +150,7 @@ form {
|
|||||||
width: 20vw;
|
width: 20vw;
|
||||||
height: 5vw;
|
height: 5vw;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
border-radius: 30px;
|
border-radius: 5vw;
|
||||||
}
|
}
|
||||||
.open_button:hover {
|
.open_button:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@ -162,4 +162,7 @@ form {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 1.5vw;
|
font-size: 1.5vw;
|
||||||
margin-top: 5%;
|
margin-top: 5%;
|
||||||
|
}
|
||||||
|
.about {
|
||||||
|
border-radius: 2vw !important;
|
||||||
}
|
}
|
||||||
@ -39,8 +39,9 @@
|
|||||||
width: 45vw;
|
width: 45vw;
|
||||||
height: 5vw;
|
height: 5vw;
|
||||||
color: #776658;
|
color: #776658;
|
||||||
border-radius: 30px;
|
border-radius: 5vw;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
font-size: 1.5vw;
|
||||||
}
|
}
|
||||||
.find_input_button {
|
.find_input_button {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
@ -49,22 +50,23 @@
|
|||||||
width: 10vw;
|
width: 10vw;
|
||||||
height: 5vw;
|
height: 5vw;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
border-radius: 30px;
|
border-radius: 5vw;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
font-size: 1.5vw;
|
||||||
}
|
}
|
||||||
.list_project_block {
|
.list_project_block {
|
||||||
margin-left: 3%;
|
margin-left: 3%;
|
||||||
border: 2px solid #694a2d;
|
border: 0.2vw solid #694a2d;
|
||||||
border-radius: 25px;
|
border-radius: 4.5vw;
|
||||||
width: 94%;
|
width: 94%;
|
||||||
height: 45vw;
|
height: 45vw;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
.list_project {
|
.list_project {
|
||||||
width: 95%;
|
width: 95%;
|
||||||
margin-left: 2.5%;
|
margin-left: 2.5%;
|
||||||
height: 95%;
|
margin-top: 2vw;
|
||||||
margin-top: 2.5%;
|
overflow-y: hidden;
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
.project_header_button {
|
.project_header_button {
|
||||||
@ -88,7 +90,7 @@
|
|||||||
.project_logo_block {
|
.project_logo_block {
|
||||||
width: 4.5vw;
|
width: 4.5vw;
|
||||||
height: 4.5vw;
|
height: 4.5vw;
|
||||||
border:2px solid #ffffff;
|
border: 0.3vw solid #ffffff;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-radius: 2vw;
|
border-radius: 2vw;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -7,14 +7,14 @@
|
|||||||
<div class="name_form_block">
|
<div class="name_form_block">
|
||||||
<div class="form_data">
|
<div class="form_data">
|
||||||
<label class="form_label">{{ form.name.label }}</label>
|
<label class="form_label">{{ form.name.label }}</label>
|
||||||
{{ form.name(class="input_data", type="name", placeholder='your project name') }}
|
{{ form.name(class="input_data label_data", type="name", placeholder='your project name') }}
|
||||||
{% for error in form.name.errors %}
|
{% for error in form.name.errors %}
|
||||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div class="form_data">
|
<div class="form_data">
|
||||||
<label class="form_label">{{ form.description.label }}</label>
|
<label class="form_label">{{ form.description.label }}</label>
|
||||||
{{ form.description(class="input_data", type="description", placeholder='your project description') }}
|
{{ form.description(class="input_data description padding_data", type="description", placeholder='your project description') }}
|
||||||
{% for error in form.description.errors %}
|
{% for error in form.description.errors %}
|
||||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -22,14 +22,24 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="data_form_block">
|
<div class="data_form_block">
|
||||||
<div class="staff_form_block">
|
<div class="staff_form_block">
|
||||||
<div class="staff_list">
|
<div class="collaborator_block">
|
||||||
|
<div class="staff_block">
|
||||||
|
{% for user in list_users %}
|
||||||
|
<div class="user">
|
||||||
|
<div class="user_data">
|
||||||
|
<img class="user_logo" src="../{{user.photo}}">
|
||||||
|
<p class="user_names">{{user.name}}</p>
|
||||||
|
</div>
|
||||||
|
<input class="choose_user" name="choose_{{user.login}}" type="checkbox" value="y">
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons_form_block">
|
<div class="buttons_form_block">
|
||||||
<div class="form_data">
|
<div class="form_data">
|
||||||
<label class="form_label">{{ form.logo.label }}</label>
|
<label class="form_label">{{ form.logo.label }}</label>
|
||||||
{{ form.logo(class="input_data file_data", type="file") }}
|
{{ form.logo(class="input_data padding_data", type="file") }}
|
||||||
{% for error in form.logo.errors %}
|
{% for error in form.logo.errors %}
|
||||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
<link rel="stylesheet" href="../static/css/page404.css"/>
|
|
||||||
{% extends "base.html" %} {% block content %}
|
|
||||||
<div class="page_404">
|
|
||||||
<div class="header_block">
|
|
||||||
<div class="header">
|
|
||||||
<hr class="line_top">
|
|
||||||
<div class="header_rect">
|
|
||||||
<strong class="header_rect_text">Ошибка 404</strong>
|
|
||||||
</div>
|
|
||||||
<hr class="line_top">
|
|
||||||
</div>
|
|
||||||
<div class="header">
|
|
||||||
<h2 class="header_title">Страница не найдена</h2>
|
|
||||||
</div>
|
|
||||||
<div class="header">
|
|
||||||
<hr class="line_bottom">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="link_block">
|
|
||||||
<div class="block_to_home">
|
|
||||||
<a class="link_to_home" href="/#header_block">
|
|
||||||
<img class="link_image" src="../static/images/logo_w.png">
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
27
templates/page_error.html
Normal file
27
templates/page_error.html
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<link rel="stylesheet" href="../../../../static/css/page_error.css"/>
|
||||||
|
{% extends "base.html" %} {% block content %}
|
||||||
|
<div class="page_error">
|
||||||
|
<div class="header_block">
|
||||||
|
<div class="header">
|
||||||
|
<hr class="line_top">
|
||||||
|
<div class="header_rect">
|
||||||
|
<strong class="header_rect_text">{{ error }}</strong>
|
||||||
|
</div>
|
||||||
|
<hr class="line_top">
|
||||||
|
</div>
|
||||||
|
<div class="header">
|
||||||
|
<h2 class="header_title">{{ message }}</h2>
|
||||||
|
</div>
|
||||||
|
<div class="header">
|
||||||
|
<hr class="line_bottom">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="link_block">
|
||||||
|
<div class="block_to_home">
|
||||||
|
<a class="link_to_home" href="/#header_block">
|
||||||
|
<img class="link_image" src="../../../../static/images/logo_w.png">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@ -64,7 +64,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form_data">
|
<div class="form_data">
|
||||||
<label class="form-label">{{ form.about.label }}</label>
|
<label class="form-label">{{ form.about.label }}</label>
|
||||||
{{ form.about(class="input_data dop_data", type="name",
|
{{ form.about(class="input_data dop_data about", type="name",
|
||||||
placeholder='about') }} {% for error in form.about.errors %}
|
placeholder='about') }} {% for error in form.about.errors %}
|
||||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@ -19,12 +19,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="list_project_block">
|
<div class="list_project_block">
|
||||||
{% for project in list_projects %}
|
{% for project in list_projects %}
|
||||||
<div class="accordion list_project" id="accordionPanelsStayOpenExample">
|
<div class="accordion list_project" id="accordionPanelsStayOpen{{ project.id }}">
|
||||||
<div class="accordion-item project">
|
<div class="accordion-item project">
|
||||||
<h2 class="accordion-header project_header" id="panelsStayOpen-headingOne">
|
<h2 class="accordion-header project_header" id="panelsStayOpen-heading{{ project.id }}">
|
||||||
<button class="accordion-button project_header_button" type="button" data-bs-toggle="collapse"
|
<button class="accordion-button project_header_button" type="button" data-bs-toggle="collapse"
|
||||||
data-bs-target="#panelsStayOpen-collapseOne" aria-expanded="true"
|
data-bs-target="#panelsStayOpen-collapse{{ project.id }}" aria-expanded="true"
|
||||||
aria-controls="panelsStayOpen-collapseOne">
|
aria-controls="panelsStayOpen-collapse{{ project.id }}">
|
||||||
<div class="project_button_block_one">
|
<div class="project_button_block_one">
|
||||||
<div class="project_logo_block">
|
<div class="project_logo_block">
|
||||||
<img src="{{ project.logo }}" class="project_logo">
|
<img src="{{ project.logo }}" class="project_logo">
|
||||||
@ -35,8 +35,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div id="panelsStayOpen-collapseOne" class="accordion-collapse collapse project_description_block"
|
<div id="panelsStayOpen-collapse{{ project.id }}" class="accordion-collapse collapse project_description_block"
|
||||||
aria-labelledby="panelsStayOpen-headingOne">
|
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">
|
||||||
<div class="staff_block">
|
<div class="staff_block">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user