separate livesupport to another blueprint
This commit is contained in:
parent
75dacd8c8a
commit
50665628c9
15 changed files with 350 additions and 99 deletions
|
@ -68,6 +68,9 @@ app.register_blueprint(smanager_blueprint, url_prefix='/smanager')
|
||||||
from .dmanager import dmanager as dmanager_blueprint
|
from .dmanager import dmanager as dmanager_blueprint
|
||||||
app.register_blueprint(dmanager_blueprint, url_prefix='/dmanager')
|
app.register_blueprint(dmanager_blueprint, url_prefix='/dmanager')
|
||||||
|
|
||||||
|
from .livesupport import livesupport as livesupport_blueprint
|
||||||
|
app.register_blueprint(livesupport_blueprint, url_prefix='/support')
|
||||||
|
|
||||||
from .uinvoice import uinvoice as uinvoice_blueprint
|
from .uinvoice import uinvoice as uinvoice_blueprint
|
||||||
app.register_blueprint(uinvoice_blueprint, url_prefix='/uinvoice')
|
app.register_blueprint(uinvoice_blueprint, url_prefix='/uinvoice')
|
||||||
|
|
||||||
|
|
3
app/livesupport/__init__.py
Normal file
3
app/livesupport/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from flask import Blueprint
|
||||||
|
livesupport = Blueprint('livesupport', __name__)
|
||||||
|
from . import routes
|
27
app/livesupport/forms.py
Normal file
27
app/livesupport/forms.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField, PasswordField, BooleanField, SubmitField, SelectField, DecimalField
|
||||||
|
from flask_pagedown.fields import PageDownField
|
||||||
|
from wtforms import validators, ValidationError
|
||||||
|
from wtforms.fields.html5 import EmailField, DecimalRangeField
|
||||||
|
|
||||||
|
from .. import db
|
||||||
|
|
||||||
|
class OrderForm(FlaskForm):
|
||||||
|
region_choices = [(1, 'Plovdiv, Bulgaria'), (2, 'International Space Station')]
|
||||||
|
region = SelectField('Region:', choices=region_choices, coerce=int)
|
||||||
|
|
||||||
|
recipe_choices = [(1, 'RootVPS')]
|
||||||
|
recipe = SelectField('Type:', choices=recipe_choices, coerce=int)
|
||||||
|
|
||||||
|
cpu = DecimalRangeField('Processor Cores', default=2)
|
||||||
|
memory = DecimalRangeField('Memory', default=2048)
|
||||||
|
storage = DecimalRangeField('Storage', default=20)
|
||||||
|
|
||||||
|
alias = StringField('Name:', [validators.Regexp(message='ex.: myservice1.com, myservice2.local', regex='^[a-zA-Z0-9][a-zA-Z0-9-_]{0,61}[a-zA-Z0-9]{0,1}\.([a-zA-Z]{1,6}|[a-zA-Z0-9-]{1,30}\.[a-zA-Z]{2,3})$'), validators.Length(6,64)])
|
||||||
|
|
||||||
|
submit = SubmitField('DEPLOY')
|
||||||
|
|
||||||
|
class MessageForm(FlaskForm):
|
||||||
|
line = PageDownField('Enter your message...', validators=[validators.DataRequired()])
|
||||||
|
submit = SubmitField('Submit')
|
||||||
|
|
83
app/livesupport/routes.py
Normal file
83
app/livesupport/routes.py
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
from flask import render_template, abort, redirect, url_for, abort, flash, request, current_app, make_response, g
|
||||||
|
from flask_login import login_required, login_user, logout_user, current_user
|
||||||
|
from flask_sqlalchemy import get_debug_queries
|
||||||
|
|
||||||
|
from . import livesupport
|
||||||
|
from .forms import MessageForm
|
||||||
|
from .. import db
|
||||||
|
from ..email import send_email
|
||||||
|
from ..models import User, Permission, SupportTopic, SupportLine, contact_proxmaster
|
||||||
|
|
||||||
|
import base64
|
||||||
|
from datetime import date, time, datetime
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
|
@livesupport.after_app_request
|
||||||
|
def after_request(response):
|
||||||
|
for query in get_debug_queries():
|
||||||
|
if query.duration >= current_app.config['SLOW_DB_QUERY_TIME']:
|
||||||
|
current_app.logger.warning('Slow query: %s\nParameters: %s\nDuration: %fs\nContext: %s\n' % (query.statement, query.parameters, query.duration, query.context))
|
||||||
|
return response
|
||||||
|
|
||||||
|
#SUPPORT
|
||||||
|
@livesupport.route("/support", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def support_list():
|
||||||
|
""" general enquiry and list all open support tasks """
|
||||||
|
cuser = current_user
|
||||||
|
form = MessageForm()
|
||||||
|
|
||||||
|
alltopics = cuser.inv_topics.all()
|
||||||
|
return render_template('livesupport/support_list.html', form=form, inv_topics=alltopics)
|
||||||
|
|
||||||
|
@livesupport.route("/support/<string:topic>/", methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def support(topic):
|
||||||
|
""" block item for support chatbox. invoked from vdc_pool or supportlist """
|
||||||
|
cuser = current_user
|
||||||
|
form = MessageForm()
|
||||||
|
|
||||||
|
if request.method == "GET":
|
||||||
|
support_topic = SupportTopic.query.filter_by(hashtag=str(topic)).first()
|
||||||
|
if support_topic == None:
|
||||||
|
class EmptySupport():
|
||||||
|
hashtag=str(topic)
|
||||||
|
timestamp=datetime.utcnow()
|
||||||
|
support_topic = EmptySupport()
|
||||||
|
return render_template('livesupport/support_item.html', form=form, support=support_topic)
|
||||||
|
else:
|
||||||
|
if support_topic.user_id != cuser.pid:
|
||||||
|
abort(403) #TODO: hidden 403. there is a topic like that but its not yours!
|
||||||
|
else:
|
||||||
|
#topic is yours. show it.
|
||||||
|
return render_template('livesupport/support_item.html', form=form, support=support_topic)
|
||||||
|
|
||||||
|
if request.method == "POST" and form.validate_on_submit():
|
||||||
|
support_topic = SupportTopic.query.filter_by(hashtag=str(topic)).first()
|
||||||
|
if support_topic == None:
|
||||||
|
#no topic. create one?
|
||||||
|
if cuser.inv_topics.all() != []:
|
||||||
|
#check if other topics exist, and ratelimit
|
||||||
|
last_topic = cuser.inv_topics.order_by(SupportTopic.timestamp.desc()).first()
|
||||||
|
now = datetime.utcnow()
|
||||||
|
time_last_topic = last_topic.timestamp
|
||||||
|
expiry = time_last_topic + relativedelta(time_last_topic, minutes=+5)
|
||||||
|
if now < expiry:
|
||||||
|
flash('ratelimit. try again later')
|
||||||
|
return redirect(url_for('livesupport.support_list'))
|
||||||
|
#create new topic
|
||||||
|
new_topic = SupportTopic(user_id=cuser.pid, hashtag=str(topic))
|
||||||
|
db.session.add(new_topic)
|
||||||
|
new_line = SupportLine(topic_id=new_topic.pid, line=str(form.line.data))
|
||||||
|
db.session.add(new_line)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if support_topic.user_id == cuser.pid:
|
||||||
|
new_line = SupportLine(topic_id=support_topic.pid, line=form.line.data)
|
||||||
|
db.session.add(new_line)
|
||||||
|
else:
|
||||||
|
abort(403) #TODO: hidden 404
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for('livesupport.support_list'))
|
||||||
|
|
|
@ -441,7 +441,7 @@ class SupportTopic(db.Model):
|
||||||
class SupportLine(db.Model):
|
class SupportLine(db.Model):
|
||||||
__tablename__ = 'support_line'
|
__tablename__ = 'support_line'
|
||||||
pid = db.Column(db.Integer, primary_key=True)
|
pid = db.Column(db.Integer, primary_key=True)
|
||||||
topic_id = db.Column(db.ForeignKey('support_topic.pid'))
|
topic_id = db.Column(db.ForeignKey('support_topic.pid')) #FK
|
||||||
line = db.Column(db.Unicode)
|
line = db.Column(db.Unicode)
|
||||||
timestamp = db.Column(db.DateTime(), default=datetime.utcnow)
|
timestamp = db.Column(db.DateTime(), default=datetime.utcnow)
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,4 @@ class OrderForm(FlaskForm):
|
||||||
|
|
||||||
submit = SubmitField('DEPLOY')
|
submit = SubmitField('DEPLOY')
|
||||||
|
|
||||||
class MessageForm(FlaskForm):
|
|
||||||
line = PageDownField('Enter your message...', validators=[validators.DataRequired()])
|
|
||||||
submit = SubmitField('Submit')
|
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from . import panel
|
||||||
from .forms import OrderForm, MessageForm
|
from .forms import OrderForm, MessageForm
|
||||||
from .. import db
|
from .. import db
|
||||||
from ..email import send_email
|
from ..email import send_email
|
||||||
from ..models import User, Permission, Recipe, Order, Server, Deployment, Service, Region, Address, Domain, SupportTopic, SupportLine, contact_proxmaster
|
from ..models import User, Permission, Recipe, Order, Server, Deployment, Service, Region, Address, Domain, contact_proxmaster
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
from datetime import date, time, datetime
|
from datetime import date, time, datetime
|
||||||
|
@ -99,63 +99,3 @@ def dashboard(user_pid):
|
||||||
continue
|
continue
|
||||||
return render_template('panel/dashboard.html', sys_regions=sys_regions, inv_deployments=inv_deployments, inv_services=inv_services, inv_domains=inv_domains, inv_addresses=inv_addresses, rrd=rrd, status=statuses, warnflag=warnflag, regions=regions)
|
return render_template('panel/dashboard.html', sys_regions=sys_regions, inv_deployments=inv_deployments, inv_services=inv_services, inv_domains=inv_domains, inv_addresses=inv_addresses, rrd=rrd, status=statuses, warnflag=warnflag, regions=regions)
|
||||||
|
|
||||||
#SUPPORT
|
|
||||||
@panel.route("/support", methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
def list_support():
|
|
||||||
""" general enquiry and list all open support tasks """
|
|
||||||
cuser = current_user
|
|
||||||
alltopics = cuser.inv_topics.all()
|
|
||||||
return render_template('panel/support_list.html', inv_topics=alltopics)
|
|
||||||
|
|
||||||
@panel.route("/support/<string:topic>/", methods=['GET', 'POST'])
|
|
||||||
@login_required
|
|
||||||
def support(topic):
|
|
||||||
""" block item for support chatbox. invoked from vdc_pool or supportlist """
|
|
||||||
cuser = current_user
|
|
||||||
form = MessageForm()
|
|
||||||
|
|
||||||
if request.method == "GET":
|
|
||||||
support_topic = SupportTopic.query.filter_by(hashtag=str(topic)).first()
|
|
||||||
if support_topic == None:
|
|
||||||
class EmptySupport():
|
|
||||||
hashtag=str(topic)
|
|
||||||
|
|
||||||
support_topic = EmptySupport()
|
|
||||||
return render_template('panel/support_item.html', form=form, support=support_topic)
|
|
||||||
else:
|
|
||||||
if support_topic.user_id != cuser.pid:
|
|
||||||
abort(403) #TODO: hidden 403. there is a topic like that but its not yours!
|
|
||||||
else:
|
|
||||||
#topic is yours. show it.
|
|
||||||
return render_template('panel/support_item.html', form=form, support=support_topic)
|
|
||||||
|
|
||||||
if request.method == "POST" and form.validate_on_submit():
|
|
||||||
support_topic = SupportTopic.query.filter_by(hashtag=str(topic)).first()
|
|
||||||
if support_topic == None:
|
|
||||||
#no topic. create one?
|
|
||||||
if cuser.inv_topics.all() != []:
|
|
||||||
#check if other topics exist, and ratelimit
|
|
||||||
last_topic = cuser.inv_topics.order_by(SupportTopic.timestamp.desc()).first()
|
|
||||||
now = datetime.utcnow()
|
|
||||||
time_last_topic = last_topic.timestamp
|
|
||||||
expiry = time_last_topic + relativedelta(time_last_topic, minutes=+5)
|
|
||||||
if now < expiry:
|
|
||||||
flash('ratelimit. try again later')
|
|
||||||
return redirect(url_for('panel.dashboard'))
|
|
||||||
#create new topic
|
|
||||||
new_topic = SupportTopic(user_id=cuser.pid, hashtag=str(topic))
|
|
||||||
db.session.add(new_topic)
|
|
||||||
new_line = SupportLine(topic_id=new_topic.pid, line=str(form.line.data))
|
|
||||||
db.session.add(new_line)
|
|
||||||
|
|
||||||
else:
|
|
||||||
if support_topic.user_id == cuser.pid:
|
|
||||||
new_line = SupportLine(topic_id=support_topic.pid, line=form.line.data)
|
|
||||||
db.session.add(new_line)
|
|
||||||
else:
|
|
||||||
abort(403) #TODO: hidden 404
|
|
||||||
|
|
||||||
db.session.commit()
|
|
||||||
return redirect(url_for('panel.dashboard'))
|
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ class EditProfileForm(FlaskForm):
|
||||||
org_account = BooleanField('This is a business account.')
|
org_account = BooleanField('This is a business account.')
|
||||||
org_companyname = StringField('Company Name:')
|
org_companyname = StringField('Company Name:')
|
||||||
org_regaddress = StringField('Company Address:')
|
org_regaddress = StringField('Company Address:')
|
||||||
org_responsible = StringField('Accountable Person (optional):')
|
org_responsible = StringField('Accountable Person:')
|
||||||
org_vatnum = StringField('VAT Number:')
|
org_vatnum = StringField('VAT Number:')
|
||||||
twofactor = BooleanField('Enable 2-factor authentication')
|
twofactor = BooleanField('Enable 2-factor authentication')
|
||||||
submit = SubmitField('Update')
|
submit = SubmitField('Update')
|
||||||
|
|
|
@ -200,7 +200,7 @@ a:active {
|
||||||
}
|
}
|
||||||
|
|
||||||
.form {
|
.form {
|
||||||
max-width: 500px;
|
max-width: 660px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-left-32 {
|
.padding-left-32 {
|
||||||
|
|
99
app/static/livesupport/chat.css
Normal file
99
app/static/livesupport/chat.css
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
.mytext{
|
||||||
|
border:0;padding:10px;background:whitesmoke;
|
||||||
|
}
|
||||||
|
.text{
|
||||||
|
width:75%;display:flex;flex-direction:column;
|
||||||
|
}
|
||||||
|
.text > p:first-of-type{
|
||||||
|
width:100%;margin-top:0;margin-bottom:auto;line-height: 13px;font-size: 12px;
|
||||||
|
}
|
||||||
|
.text > p:last-of-type{
|
||||||
|
width:100%;text-align:right;color:silver;margin-bottom:-7px;margin-top:auto;
|
||||||
|
}
|
||||||
|
.text-l{
|
||||||
|
float:left;padding-right:10px;
|
||||||
|
}
|
||||||
|
.text-r{
|
||||||
|
float:right;padding-left:10px;
|
||||||
|
}
|
||||||
|
.avatar{
|
||||||
|
display:flex;
|
||||||
|
justify-content:center;
|
||||||
|
align-items:center;
|
||||||
|
width:25%;
|
||||||
|
float:left;
|
||||||
|
padding-right:10px;
|
||||||
|
}
|
||||||
|
.macro{
|
||||||
|
margin-top:5px;width:85%;border-radius:5px;padding:5px;display:flex;
|
||||||
|
}
|
||||||
|
.msj-rta{
|
||||||
|
float:right;background:whitesmoke;
|
||||||
|
}
|
||||||
|
.msj{
|
||||||
|
float:left;background:white;
|
||||||
|
}
|
||||||
|
.frame{
|
||||||
|
background:#e0e0de;
|
||||||
|
height:450px;
|
||||||
|
overflow:hidden;
|
||||||
|
padding:0;
|
||||||
|
}
|
||||||
|
.frame > div:last-of-type{
|
||||||
|
position:absolute;bottom:0;width:100%;display:flex;
|
||||||
|
}
|
||||||
|
body > div > div > div:nth-child(2) > span{
|
||||||
|
background: whitesmoke;padding: 10px;font-size: 21px;border-radius: 50%;
|
||||||
|
}
|
||||||
|
body > div > div > div.msj-rta.macro{
|
||||||
|
margin:auto;margin-left:1%;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
width:100%;
|
||||||
|
list-style-type: none;
|
||||||
|
padding:18px;
|
||||||
|
position:absolute;
|
||||||
|
bottom:47px;
|
||||||
|
display:flex;
|
||||||
|
flex-direction: column;
|
||||||
|
top:0;
|
||||||
|
overflow-y:scroll;
|
||||||
|
}
|
||||||
|
.msj:before{
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
content:"";
|
||||||
|
top:-5px;
|
||||||
|
left:-14px;
|
||||||
|
position:relative;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0 13px 13px 0;
|
||||||
|
border-color: transparent #ffffff transparent transparent;
|
||||||
|
}
|
||||||
|
.msj-rta:after{
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
content:"";
|
||||||
|
top:-5px;
|
||||||
|
left:14px;
|
||||||
|
position:relative;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 13px 13px 0 0;
|
||||||
|
border-color: whitesmoke transparent transparent transparent;
|
||||||
|
}
|
||||||
|
input:focus{
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
::-webkit-input-placeholder { /* Chrome/Opera/Safari */
|
||||||
|
color: #d4d4d4;
|
||||||
|
}
|
||||||
|
::-moz-placeholder { /* Firefox 19+ */
|
||||||
|
color: #d4d4d4;
|
||||||
|
}
|
||||||
|
:-ms-input-placeholder { /* IE 10+ */
|
||||||
|
color: #d4d4d4;
|
||||||
|
}
|
||||||
|
:-moz-placeholder { /* Firefox 18- */
|
||||||
|
color: #d4d4d4;
|
||||||
|
}
|
||||||
|
|
83
app/static/livesupport/chat.js
Normal file
83
app/static/livesupport/chat.js
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
var me = {};
|
||||||
|
me.avatar = "https://lh6.googleusercontent.com/-lr2nyjhhjXw/AAAAAAAAAAI/AAAAAAAARmE/MdtfUmC0M4s/photo.jpg?sz=48";
|
||||||
|
|
||||||
|
var you = {};
|
||||||
|
you.avatar = "https://a11.t26.net/taringa/avatares/9/1/2/F/7/8/Demon_King1/48x48_5C5.jpg";
|
||||||
|
|
||||||
|
function formatAMPM(date) {
|
||||||
|
var hours = date.getHours();
|
||||||
|
var minutes = date.getMinutes();
|
||||||
|
var ampm = hours >= 12 ? 'PM' : 'AM';
|
||||||
|
hours = hours % 12;
|
||||||
|
hours = hours ? hours : 12; // the hour '0' should be '12'
|
||||||
|
minutes = minutes < 10 ? '0'+minutes : minutes;
|
||||||
|
var strTime = hours + ':' + minutes + ' ' + ampm;
|
||||||
|
return strTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-- No use time. It is a javaScript effect.
|
||||||
|
function insertChat(who, text, time){
|
||||||
|
if (time === undefined){
|
||||||
|
time = 0;
|
||||||
|
}
|
||||||
|
var control = "";
|
||||||
|
var date = formatAMPM(new Date());
|
||||||
|
|
||||||
|
if (who == "me"){
|
||||||
|
control = '<li style="width:100%">' +
|
||||||
|
'<div class="msj macro">' +
|
||||||
|
'<div class="avatar"><img class="img-circle" style="width:100%;" src="'+ me.avatar +'" /></div>' +
|
||||||
|
'<div class="text text-l">' +
|
||||||
|
'<p>'+ text +'</p>' +
|
||||||
|
'<p><small>'+date+'</small></p>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</li>';
|
||||||
|
}else{
|
||||||
|
control = '<li style="width:100%;">' +
|
||||||
|
'<div class="msj-rta macro">' +
|
||||||
|
'<div class="text text-r">' +
|
||||||
|
'<p>'+text+'</p>' +
|
||||||
|
'<p><small>'+date+'</small></p>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="avatar" style="padding:0px 0px 0px 10px !important"><img class="img-circle" style="width:100%;" src="'+you.avatar+'" /></div>' +
|
||||||
|
'</li>';
|
||||||
|
}
|
||||||
|
setTimeout(
|
||||||
|
function(){
|
||||||
|
$("ul").append(control).scrollTop($("ul").prop('scrollHeight'));
|
||||||
|
}, time);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetChat(){
|
||||||
|
$("ul").empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
$(".mytext").on("keydown", function(e){
|
||||||
|
if (e.which == 13){
|
||||||
|
var text = $(this).val();
|
||||||
|
if (text !== ""){
|
||||||
|
insertChat("me", text);
|
||||||
|
$(this).val('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('body > div > div > div:nth-child(2) > span').click(function(){
|
||||||
|
$(".mytext").trigger({type: 'keydown', which: 13, keyCode: 13});
|
||||||
|
})
|
||||||
|
|
||||||
|
//-- Clear Chat
|
||||||
|
resetChat();
|
||||||
|
|
||||||
|
//-- Print Messages
|
||||||
|
insertChat("me", "Hello Tom...", 0);
|
||||||
|
insertChat("you", "Hi, Pablo", 1500);
|
||||||
|
insertChat("me", "What would you like to talk about today?", 3500);
|
||||||
|
insertChat("you", "Tell me a joke",7000);
|
||||||
|
insertChat("me", "Spaceman: Computer! Computer! Do we bring battery?!", 9500);
|
||||||
|
insertChat("you", "LOL", 12000);
|
||||||
|
|
||||||
|
|
||||||
|
//-- NOTE: No use time on insertChat.
|
39
app/templates/livesupport/support_item.html
Normal file
39
app/templates/livesupport/support_item.html
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
{% block support_item %}
|
||||||
|
{% if support.inv_lines != [] %}
|
||||||
|
<div class="col-sm-3 col-sm-offset-4 frame">
|
||||||
|
<ul></ul>
|
||||||
|
<div>
|
||||||
|
<div class="msj-rta macro">
|
||||||
|
<div class="text text-r" style="background:whitesmoke !important">
|
||||||
|
<input class="mytext" placeholder="Type a message"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="glyphicon glyphicon-share-alt"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-default" style="margin-top: 0px">
|
||||||
|
<div class="panel-heading" href="#support{{ support.pid }}" aria-expanded="true" aria-controls="support{{ support.pid }}" role="tab" id="support{{ support.pid }}">
|
||||||
|
<font color="green">started at {{ moment(support.timestamp).format('ll') }}</font></b></a>
|
||||||
|
</div>
|
||||||
|
</div> <!-- end of heading -->
|
||||||
|
|
||||||
|
<br />
|
||||||
|
{% for line in support.inv_lines %}
|
||||||
|
<div align="right">{{ line.topic.owner.email }} {{ moment(line.timestamp).format('lll') }}</div>
|
||||||
|
{{ line.line }} <br />
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="POST" action="{{ url_for('panel.support', topic=support.hashtag) }}">
|
||||||
|
{{ form.line | safe }}
|
||||||
|
{% for error in form.line.errors %}
|
||||||
|
{{ error }}<br />
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{{ form.csrf_token() }}
|
||||||
|
{{ form.submit }}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -19,18 +19,19 @@ $('a[data-toggle="tooltip"]').tooltip({
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
{% if inv_topics != [] %}
|
{% if inv_topics != [] %}
|
||||||
|
{% for support in inv_topics %}
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="panel panel-info" id="deployments">
|
<div class="panel panel-info" id="deployments">
|
||||||
<div class="panel-heading">Support</div>
|
<div class="panel-heading">{{ support.hashtag }}</div>
|
||||||
<div class="panel-body"><p>
|
<div class="panel-body"><p>
|
||||||
<div class="panel-group" id="<s" role="tablist" aria-multiselectable="true">
|
<div class="panel-group" id="support{{ support.pid }}" role="tablist" aria-multiselectable="true">
|
||||||
{% for support in inv_topics %}
|
|
||||||
{% include "panel/support_item.html" %}
|
{% include "panel/support_item.html" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
|
@ -52,7 +52,7 @@
|
||||||
<li><a href="{{ url_for('uinvoice.transactions') }}"><span class="glyphicon glyphicon-list-alt"></span> Transactions</a></li>
|
<li><a href="{{ url_for('uinvoice.transactions') }}"><span class="glyphicon glyphicon-list-alt"></span> Transactions</a></li>
|
||||||
<li role="separator" class="divider"></li>
|
<li role="separator" class="divider"></li>
|
||||||
<li><a href="{{ url_for('settings.profile') }}"><span class="glyphicon glyphicon-user"></span> Profile</a></li>
|
<li><a href="{{ url_for('settings.profile') }}"><span class="glyphicon glyphicon-user"></span> Profile</a></li>
|
||||||
<li><a href="{{ url_for('main.chat') }}" target="_blank"><span class="glyphicon glyphicon-question-sign"></span> Live Chat</a></li>
|
<li><a href="{{ url_for('livesupport.support_list') }}"><span class="glyphicon glyphicon-question-sign"></span> Support</a></li>
|
||||||
<li><a href="{{ url_for('auth.logout') }}"><span class="glyphicon glyphicon-off"></span> Logout</a></li>
|
<li><a href="{{ url_for('auth.logout') }}"><span class="glyphicon glyphicon-off"></span> Logout</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
{% block support_item %}
|
|
||||||
<div class="panel panel-danger" style="margin-top: 2px">
|
|
||||||
<div class="panel-heading" href="#support{{ support.pid }}" aria-expanded="true" aria-controls="support{{ support.pid }}" role="tab" id="support{{ support.pid }}">
|
|
||||||
<font color="green">{{ support.hashtag }} started at {{ support.timestamp }}</font></b></a>
|
|
||||||
i </div>
|
|
||||||
</div> <!-- end of heading -->
|
|
||||||
|
|
||||||
<br />
|
|
||||||
{% for line in inv_lines %}
|
|
||||||
{{ line.timestamp }}
|
|
||||||
{{ line.line }} <br />
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
<form method="POST" action="{{ url_for('panel.support', topic=support.hashtag) }}">
|
|
||||||
{{ form.line | safe }}
|
|
||||||
{% for error in form.line.errors %}
|
|
||||||
{{ error }}<br />
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{{ form.csrf_token() }}
|
|
||||||
{{ form.submit }}
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
Loading…
Reference in a new issue