remotes/origin/PR-39
ArturBaybulatov 9 years ago
commit 61c05d93e2
  1. 8
      assets/css/main.css
  2. 86
      assets/js/chat.js
  3. 25
      assets/js/chat_contractor.js
  4. 142
      assets/js/chat_customer.js
  5. 1
      chat/serializers.py
  6. 1
      chat/templates/chat_contractor.html
  7. 20
      projects/models.py
  8. 10
      projects/serializers.py
  9. 2
      templates/partials/base.html
  10. 5
      users/models.py
  11. 237
      users/templates/contractor_office.html
  12. 26
      users/templates/customer_profile_reviews.html
  13. 29
      users/templatetags/user_tags.py

@ -237,14 +237,14 @@ ul li {
}
.changeBlock1 > a, .changeBlock1 > a:link, .changeBlock1 > a:visited {
background: url('../img/button1.png') no-repeat 25px , white;
color: black;
background: url('../img/button1.png') no-repeat 25px , black;
color: white;
padding: 24px 26px 20px 72px;
}
.changeBlock2 > a, .changeBlock2 > a:link, .changeBlock2 > a:visited {
background: url('../img/button2.png') no-repeat 27px, black;
color: white;
background: url('../img/button2.png') no-repeat 27px, white;
color: black;
padding: 24px 26px 20px 72px;
}

@ -1,6 +1,5 @@
window.confirm = function (message, callback, caption) {
caption = caption || ''
$(document.createElement('div')).attr({
title: caption,
'class': 'dialog'
@ -20,7 +19,6 @@ window.confirm = function (message, callback, caption) {
callback()
return true;
}
},
close: function () {
$(this).remove();
@ -31,7 +29,7 @@ window.confirm = function (message, callback, caption) {
var SocketHandler = function () {
domain = domain.replace(':' + port, '');
var url = 'ws://' + domain + ':8888/chat/' + userId + '/';
var url = 'ws://' + domain + '/chat/' + userId + '/';
var sock = new WebSocket(url);
var intervalId;
sock.onopen = function () {
@ -109,22 +107,6 @@ var socket = new SocketHandler();
var csrftoken = getCookie('csrftoken');
function test_dialog(message){
var resStatus;
$("#dialog_delete .modal-title").html(message);
$("#dialog_delete").modal('show');
$("#btnYes").click(function (e) {
$("#dialog_delete").modal('hide');
resStatus = true;
});
$("#btnNot").click(function (e) {
$("#dialog_delete").modal('hide');
resStatus = false;
});
return resStatus;
}
$(function () {
function dialog (message, yesCallback, notCallback) {
$("#dialog_delete .modal-title").html(message);
@ -682,33 +664,6 @@ function dialog (message, yesCallback, notCallback) {
});
// Добавление отзыва
$('#order-review-add').on('click', function (e) {
e.preventDefault();
e.stopPropagation();
var formData = $("#review-adds-form").serialize();
$.ajax({
url: '/api/reviews/',
type: 'POST',
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
data: formData,
dataType: 'json',
success: function (json) {
console.log("Успешно");
$("#review-add").modal('hide');
$.jGrowl("Ваш отзыв успешно добавлен", {
life: 4000
});
},
error: function (e) {
console.log('error');
console.log(e);
}
});
});
// Добавление сообщения в арбитраж
$('#order-arbitration-add').on('click', function (e) {
e.preventDefault();
@ -761,7 +716,8 @@ function dialog (message, yesCallback, notCallback) {
dataType: 'json',
done: function (e, data) {
$.each(data.result.files, function (index, file) {
var htmlImg = '<a href="/chat/download/' + file.name + '" class="send-doc" data-id="' + file.id + '">' + file.name + '</a><div class="remove-document" data-id="'+ file.id+'" style="right:-10px;"></div><br />';
var htmlImg = '<div style="float: left"><a href="/chat/download/' + file.name + '" class="send-doc" data-id="' + file.id + '">' + file.name + '</a>' +
'<div class="remove-document" data-id="'+ file.id+'" style="right:-10px;float:left;position: static;"></div></div>';
var document_send = $(htmlImg).appendTo("#document-send-order");
});
},
@ -802,7 +758,8 @@ function dialog (message, yesCallback, notCallback) {
dataType: 'json',
done: function (e, data) {
$.each(data.result.files, function (index, file) {
var htmlImg = '<a href="/chat/download/' + file.name + '" class="send-doc" data-id="' + file.id + '">' + file.name + '</a>';
var htmlImg = '<div style="float: left"><a href="/chat/download/' + file.name + '" class="send-doc" data-id="' + file.id + '">' + file.name + '</a>' +
'<div class="remove-document" data-id="'+ file.id+'" style="right:-10px;float:left;position: static;"></div></div>';
var document_send = $(htmlImg).appendTo("#document-send-contact");
});
},
@ -824,6 +781,39 @@ function dialog (message, yesCallback, notCallback) {
var relatedType = related.attr('data-review-type');
$('input[name="type"]').filter('[value="'+ relatedType+'"]').prop("checked", true);
});
// Добавление отзыва
$('#order-review-add').on('click', function (e) {
e.preventDefault();
e.stopPropagation();
var formData = $("#review-adds-form").serialize();
$.ajax({
url: '/api/reviews/',
type: 'POST',
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
data: formData,
dataType: 'json',
success: function (json) {
console.log(json);
$("#review-add").modal('hide');
//socket.send_stages_approve({
// "format_type": "approve_stages",
// "data": {
// "sender_id": userId,
// "recipent_id": '',
// "order_id": '',
// "msg": "Отзыв добавлен",
// }
//});
},
error: function (e) {
console.log('error');
console.log(e);
}
});
});
});

@ -48,7 +48,8 @@ $(function () {
var currentValue = $("#documentSendIds").val();
currentValue += file.id + ';';
$("#documentSendIds").val(currentValue);
var htmlImg = '<a href="/chat/download/' + file.name + '" class="send-doc" data-id="' + file.id + '">' + file.name + '</a>';
var htmlImg = '<div style="float: left"><a href="/chat/download/' + file.name + '" class="send-doc" data-id="' + file.id + '">' + file.name + '</a>' +
'<div class="remove-document" data-id="'+ file.id+'" style="right:-10px;float:left;position: static;"></div></div>';
var document_send = $(htmlImg).appendTo("#document-send");
});
},
@ -82,8 +83,7 @@ $(function () {
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
//data: "status=in_process",
data: "status=send_approve",
data: "status=in_process",
dataType: 'json',
done: function (json) {
console.log(json);
@ -127,8 +127,8 @@ $(function () {
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
//data: "status=cancel_approve",
data: "status=send_approve",
data: "status=cancel_approve",
//data: "status=send_approve",
dataType: 'json',
done: function (json) {
console.log(json);
@ -438,6 +438,7 @@ $(function () {
var stagesCompleted = [];
if (stagesResults.length > 0) {
$.each(stagesResults, function (i, v) {
console.log(v.status);
if (v.status == "completed") {
stagesCompleted.push(v);
}
@ -488,7 +489,7 @@ $(function () {
'<p class="titleNumStepp"><span>Этап ' + v.pos + '</span>' + v.name + '</p>' +
'<p class="textNumStepp">Результаты этапа:' + v.result + '</p><div>' +
'<p> Срок до ' + v.term + '</p><span>' + v.cost + ' <i class="fa fa-rub"></i></span>' +
'<p>Cрок заказа рассчитывается с момента резервирования средств</p>' +
'<p>Cрок этапа рассчитывается с момента резервирования средств</p>' +
'</div><div><p>' + statusName + '</p></div></div></div>';
});
@ -534,13 +535,11 @@ $(function () {
$("#completeWork").hide();
}
if (stagesCompleted.length == stagesResults.length && stagesCompleted.length > 0) {
$("#leaveReview").show();
}
if (data.status == 'completed') {
$("#leaveReview").hide();
}
if ((stagesCompleted.length == stagesResults.length) && stagesCompleted.length > 0 && !data.has_user_review) {
$("#leaveReview").show();
}else{
$("#leaveReview").hide();
}
});

@ -99,12 +99,12 @@ $(function () {
if (stageCount == 0) {
htmlInboxStage += '<div class="numberStepp box-sizing" id="stage1">' +
'<p>Этап <span class="stage-span-id">1</span></p><form class="new-stages-form" id="stage-form">' +
'<label for="">Название</label><input class="form-control" name="name" type="text">' +
'<label for="">Цена</label><input class="form-control" name="cost" type="text">' +
'<label>Название</label><input class="form-control" name="name" type="text"><p class="error error-name"></p>' +
'<label>Цена</label><input class="form-control" name="cost" type="text"><p class="error error-cost"></p>' +
'<input class="form-control orderStagesInput" name="order" type="hidden" value="' + orderId + '">' +
'<input class="form-control" type="hidden" name="status" value="send_approve">' +
'<label for="">Срок</label><input class="term-picker form-control datepicker" name="term" type="text">' +
'<label for="">Результат</label><input class="form-control" name="result" type="text">' +
'<label>Срок</label><input class="term-picker form-control datepicker" name="term" type="text"><p class="error error-term"></p>' +
'<label>Результат</label><input class="form-control" name="result" type="text"><p class="error error-result"></p>' +
'<input class="form-control" name="pos" value="1" type="hidden">' +
'</form></div>';
}
@ -135,12 +135,13 @@ $(function () {
if (v.status == "not_agreed" || v.status == 'cancel_approve' || v.status == 'send_approve') {
htmlInbox += '<div class="numberStepp box-sizing">' +
'<p>Этап</p><form class="update-stages-form" data-stage-id="' + v.id + '" id="stage-form-' + v.pos + '">' +
'<label for="">Название</label><input class="form-control" type="text" name="name" value="' + v.name + '">' +
'<label for="">Цена</label><input class="form-control" type="text" name="cost" value="' + v.cost + '" >' +
'<label>Название</label><input class="form-control" type="text" name="name" value="' + v.name + '"><p class="error error-name"></p>' +
'<label>Цена</label><input class="form-control" type="text" name="cost" value="' + v.cost + '" ><p class="error error-cost"></p>' +
'<label>Позиция</label><input class="form-control" name="pos" type="text" value="' + v.pos + '" ><p class="error error-pos"></p>' +
'<input class="form-control orderStagesInput" type="hidden" name="order" value="' + v.order + '">' +
'<input class="form-control" type="hidden" name="status" value="send_approve">' +
'<label for="">Срок</label><input class="term-picker form-control datepicker" type="text" name="term" value="' + v.term + '" />' +
'<label for="">Результат</label><input class="form-control" type="text" name="result" value="' + v.result + '" >' +
'<label>Срок</label><input class="term-picker form-control datepicker" type="text" name="term" value="' + v.term + '" ><p class="error error-term"></p>' +
'<label>Результат</label><input class="form-control" type="text" name="result" value="' + v.result + '" ><p class="error error-result"></p>' +
'</form></div>';
} else {
statusNotAgreed = false;
@ -226,12 +227,15 @@ $(function () {
$("#reserveSpace").hide();
}
if ((stagesCompleted.length == stagesResults.length) && (stagesCompleted.length > 0) && (!isReviewLeave)) {
$("#leaveReview").show();
if (!data.has_user_review) {
if ((stagesCompleted.length == stagesResults.length) && (stagesCompleted.length > 0) && (!isReviewLeave)) {
$("#leaveReview").show();
} else {
$("#leaveReview").hide();
}
} else {
$("#leaveReview").hide();
}
$(".stages-paid").html(stagesReservedHtml);
});
@ -297,80 +301,75 @@ $(function () {
var callbacks = [];
$(".new-stages-form").each(function (i, v) {
var _this = $(this);
callbacks.push($.ajax({
url: '/api/stages/',
type: 'POST',
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
data: $(this).serialize(),
dataType: 'json',
done: function (json) {
$.ajax({
url: '/api/stages/',
type: 'POST',
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
data: $(this).serialize(),
dataType: 'json'
})
.done(function (json) {
_this.removeClass('new-stages-form').addClass('update-stages-form');
_this.find('.error').html("");
console.log(json);
},
fail: function (xhr, errorMsg,error) {
$.each(xhr.responseJSON, function(i,v){
})
.fail(function (xhr, errorMsg, error) {
console.log(xhr);
$.each(xhr.responseJSON, function (i, v) {
_this.find('.error-' + i).html(v).css('color', 'red');
console.log(v);
console.log(i);
});
},
}));
});
});
//$.when(callbacks).then(function(x){
// console.log(x);
// $.each(x,function(i,v){
// console.log(v);
// });
// alert("Запросы успешно выполнились");
//}, function(){
// alert("Произошла ошибка в запросах");
//});
$(".update-stages-form").each(function (i, v) {
var _this = $(this);
var currentStageId = parseInt($(this).attr('data-stage-id'));
$.ajax({
url: '/api/stages/' + currentStageId + '/',
type: 'PUT',
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
data: $(this).serialize(),
dataType: 'json',
success: function (json) {
console.log(json);
},
error: function (e) {
console.log('error');
console.log(e);
}
});
url: '/api/stages/' + currentStageId + '/',
type: 'PUT',
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'))
},
data: $(this).serialize(),
dataType: 'json'
})
.done(function (json) {
_this.find('.error').html("");
})
.fail(function (xhr, errorMsg, error) {
$.each(xhr.responseJSON, function (i, v) {
_this.find('.error-' + i).html(v).css('color', 'red');
console.log(v);
console.log(i);
});
});
});
var currentRecipentId = $(this).attr('data-recipent-id');
var secureOrder = true
//socket.send_stages_approve({
// "format_type": "approve_stages",
// "data": {
// "sender_id": userId,
// "recipent_id": currentRecipentId,
// "order_id": currentOrderId,
// "msg": "Этапы для заказа " + currentOrderId + " изменены",
// }
//});
socket.send_stages_approve({
"format_type": "approve_stages",
"data": {
"sender_id": userId,
"recipent_id": currentRecipentId,
"order_id": currentOrderId,
"msg": "Этапы для заказа " + currentOrderId + " изменены",
}
});
});
//Изменение счетчика
//Изменение счетчика
$('#order-stages-tab').on('change', '#countStage', function () {
var countStage = parseInt($(this).val());
var currentCountStage = $("#order-stages .numberStepp").length;
@ -385,13 +384,13 @@ $(function () {
var orderId = lastFormStage.find('.orderStagesInput').val();
var addFormTemplate = '<div class="numberStepp box-sizing" id="stage1">' +
'<p>Этап <span class="stage-span-id">' + pos + '</span></p><form class="new-stages-form" id="stage-form">' +
'<label for="">Название</label><input class="form-control" name="name" type="text">' +
'<label for="">Цена</label><input class="form-control" name="cost" type="text">' +
'<label>Название</label><input class="form-control" name="name" type="text"><p class="error error-name"></p>' +
'<label>Цена</label><input class="form-control" name="cost" type="text"><p class="error error-cost"></p>' +
'<input class="form-control orderStagesInput" name="order" type="hidden" value="' + orderId + '">' +
'<input class="form-control" type="hidden" name="status" value="send_approve">' +
'<label for="">Срок</label><input class="term-picker form-control datepicker" name="term" type="text">' +
'<label for="">Результат</label><input class="form-control" name="result" type="text">' +
'<input class="form-control" name="pos" value="' + pos + '" type="hidden"></form></div>';
'<label>Срок</label><input class="term-picker form-control datepicker" name="term" type="text"><p class="error error-term"></p>' +
'<label>Результат</label><input class="form-control" name="result" type="text"><p class="error error-result"></p>' +
'<input class="form-control" name="pos" value="' + pos + '" type="text"></form></div>';
lastFormStage.after(addFormTemplate);
}
@ -409,7 +408,7 @@ $(function () {
}
});
// Для заказов все вытащить
// Для заказов все вытащить
$('.order-block').on('click', function () {
$("#chat-order-add").css("display", "block");
$("#formsetStage").css("display", "block");
@ -465,7 +464,7 @@ $(function () {
});
$.ajax({
url: '/api/users/{{ request.user.pk }}/',
url: '/api/users/' + userId + '/',
type: 'GET',
data: {
csrfmiddlewaretoken: csrftoken,
@ -520,4 +519,5 @@ $(function () {
getStages(orderId, userId, recipentId, secureOrder);
});
});
})
;

@ -44,6 +44,7 @@ class MessageSerializer(ModelSerializer):
documents = DocumentsSerializer(read_only=True, many=True)
text = serializers.SerializerMethodField()
class Meta:
model = Message

@ -235,7 +235,6 @@
</div>
<!-- End block Tab2-->
<!-- Tab3 groups block -->
<div class="chatBlock disTab tab-pane fade" id="tab3">
<div class="col-lg-3 wrMessages">

@ -2,6 +2,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import Q
from django.core.exceptions import ValidationError
from django.utils import timezone
from hitcount.models import HitCountMixin
from mptt.models import TreeForeignKey
@ -262,7 +263,7 @@ class Stage(models.Model):
name = models.CharField(max_length=255)
order = models.ForeignKey(Order, related_name='stages')
result = models.CharField(max_length=255)
term = models.DateField(validators=[validate_term])
term = models.DateField()
term_type = models.CharField(max_length=10, choices=TERM_TYPES, default='hour')
status = models.CharField(choices=STATUSES, max_length=30, default='not_agreed')
created = models.DateTimeField(default=timezone.now)
@ -275,12 +276,17 @@ class Stage(models.Model):
def __str__(self):
return self.name
# def clean(self, *args, **kwargs):
# super().clean(*args, **kwargs)
#
# def save(self, *args, **kwargs):
# # self.full_clean()
# super().save(*args, **kwargs)
def clean(self, *args, **kwargs):
pass
# stage_last = self.__class__.objects.filter(order=self.order).order_by('-pos')[:1]
# if stage_last:
# stage_last = stage_last[0]
# if stage_last.term > self.term:
# raise ValidationError({'term':'Дата не должна быть меньше даты предыдущео этапа'})
def save(self, *args, **kwargs):
self.full_clean()
super().save(*args, **kwargs)
class Meta:
ordering = ['pos']

@ -79,6 +79,16 @@ class RealtySerializer(ModelSerializer):
class StageSerializer(ModelSerializer):
term = serializers.DateField(format="%d.%m.%Y", input_formats=['%d.%m.%Y',])
def validate(self, data):
if hasattr(data, 'pos') and data['pos'] > 1:
pos = data['pos'] -1
stage_last = Stage.objects.filter(order=data['order'], pos=pos)
if stage_last:
stage_last = stage_last[0]
if stage_last.term > data['term']:
raise serializers.ValidationError({'term':'Дата не должна быть меньше даты предыдущео этапа'})
return data
class Meta:
model = Stage

@ -86,7 +86,7 @@
if ((queryString.indexOf('/chat') != 0) && (queryString.indexOf('/users/contractor-office/510/work-projects') != 0)) {
domain = domain.replace(':' + port, '');
var url = 'ws://' + domain + ':8888/chat/' + userId + '/';
var url = 'ws://' + domain + '/chat/' + userId + '/';
var sock = new WebSocket(url);
var intervalId;
sock.onopen = function () {

@ -11,7 +11,7 @@ import pydash as _; _.map = _.map_; _.filter = _.filter_
from archilance import util
from specializations.models import Specialization
# from chat.models import NewMessage
GENDERS = (
@ -238,7 +238,7 @@ class Team(models.Model):
specializations = TreeManyToManyField(Specialization, related_name='teams', blank=True)
contractors = models.ManyToManyField(User, limit_choices_to={'groups__name': 'Исполнители'}, related_name ='teams', blank=True)
rating = models.FloatField(default=0.0)
def __str__(self):
return self.name
@ -247,6 +247,7 @@ class Team(models.Model):
verbose_name_plural = 'Команды'
class TeamInvitation(models.Model):
contractor1 = models.ForeignKey(User, related_name='invites')
contractor2 = models.ForeignKey(User, related_name='invited_by')

@ -2,42 +2,44 @@
{% load specializtions_tags %}
{% load thumbnail %}
{% load user_tags %}
{% block content %}
{% include 'partials/modals/add_team_member.html' %}
{% include 'partials/header.html' %}
<div class="container mainScore">
<div class="row">
<div class="col-lg-12">
<p class="titleScore">Личный кабинет</p>
</div>
{% include 'partials/contractor_profile_tabs.html' %}
<div class="buttonGP disTab">
<div class="btn-group valProject2 val-pro3" role="group">
{% if contractor.team %}
<a href="{% url 'users:team-profile' pk=contractor.team.pk %}" class="btn btn-default">
{{ contractor.team.name }}
<span><mark>0</mark></span>
<span><mark>{% get_new_count_message contractor.team request.user %}</mark></span>
</a>
{% else %}
<button type="button" class="btn btn-default add-group" data-toggle="modal" data-target="#myModal">
<button type="button" class="btn btn-default add-group" data-toggle="modal"
data-target="#myModal">
+ Добавить группу
</button>
{% endif %}
</div>
<div class="btn-group valProject2 val-pro3" role="group">
{% for team in contractor.teams.all %}
<a href="{% url 'users:team-profile' pk=team.pk %}" class="btn btn-default">
{{ team.name }}
<span><mark>{% get_new_count_message team request.user %}</mark></span>
</a>
{% endfor %}
</div>
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
@ -45,18 +47,19 @@
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title" id="myModalLabel">Добавление новой группы</h4>
</div>
<form method="post" action="{% url 'users:team-create' %}">{% csrf_token %}
<div class="modal-body">
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Название <span style="color: red">{{ form_team.name.errors.as_text }}</span></p>
<p>Название <span style="color: red">{{ form_team.name.errors.as_text }}</span>
</p>
<input type="text" class="box-sizing" name="{{ form_team.name.html_name }}">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary">Сохранить</button>
@ -66,7 +69,7 @@
</div>
</div>
</div>
{% if contractor.team %}
<div class="projectsBlock disTab">
<div class="col-lg-12">
@ -75,22 +78,25 @@
<div class="avatarInset">
{% if contractor.avatar %}
{% thumbnail contractor.avatar "265x264" crop="center" as im %}
<a href="{% url 'users:team-profile' pk=contractor.team.pk %}"><img src="{{ im.url }}" alt="profile-image"></a>
<a href="{% url 'users:team-profile' pk=contractor.team.pk %}"><img
src="{{ im.url }}" alt="profile-image"></a>
{% endthumbnail %}
{% else %}
<a href="{% url 'users:team-profile' pk=contractor.team.pk %}"><img src="{% static 'img/profile.jpg' %}" alt="profile-image"></a>
<a href="{% url 'users:team-profile' pk=contractor.team.pk %}"><img
src="{% static 'img/profile.jpg' %}" alt="profile-image"></a>
{% endif %}
</div>
</div>
<div class="menuUser disTab">
<a href="#" onclick="return false" data-toggle="modal" data-target="#addTeamMemberModal" class="add-man -add-team-member">
<a href="#" onclick="return false" data-toggle="modal" data-target="#addTeamMemberModal"
class="add-man -add-team-member">
добавить участника
</a>
<div class="block-users">
<p>Состав группы</p>
{% for c in contractor.team.contractors.all %}
<div class="message-new">
<div class="imgMess">
@ -102,11 +108,11 @@
<img src="{% static 'img/profile.jpg' %}" alt="mess-image">
{% endif %}
</div>
<p class="nameMess" style="float: none">
<a href="{% url 'users:contractor-profile' c.pk %}">{{ c.get_full_name }}</a>
</p>
<p><span>{{ c.get_popular_specialization }}</span></p>
</div>
{% empty %}
@ -115,31 +121,33 @@
</div>
</div>
</div>
<div class="col-lg-9 divCol9">
<div class="col-lg-4">
<p class="nameUser"><a href="{% url 'users:team-profile' pk=contractor.team.pk %}">{{ contractor.team.name }}</a></p>
<p class="nameUser"><a
href="{% url 'users:team-profile' pk=contractor.team.pk %}">{{ contractor.team.name }}</a>
</p>
<p class="cityUser">{{ contractor.get_location }}</p>
<p class="navv">На сайте {{ contractor.team.created }}</p>
<p class="navv">
Кол-во человек: <span>{{ contractor.team.contractors.count }}</span>
</p>
<p class="navv">
Выполненных проектов: <span>{{ completed_project_count }}</span>
</p>
<div class="statusUser st-new">Свободен</div>
</div>
<div class="col-lg-4">
{% specialization_team_widget contractor.team.pk %}
</div>
<div class="col-lg-4">
{% ratings_team_widget contractor.team.pk %}
{% if contractor.cro %}
<div class="sroUser">
<div class="iconSRO"></div>
@ -148,7 +156,7 @@
{% endif %}
</div>
</div>
<div class="col-lg-9">
<div class="profileTabs2">
<ul class="nav nav-tabs nav-justified">
@ -164,41 +172,49 @@
</ul>
</div>
</div>
<div class="tab-content">
<div id="tab11" class="tab-pane fade in active">
<div class="galleryWork2 disTab col-lg-9 -portfolios-container">
<script type="text/x-template" class="-portfolio-item-templ">
<div class="col-lg-4">
<div class="insetCol box-sizing disTab">
<div class="imgGal" style="background:rgba(0, 0, 0, 0) url('<%- portfolio.photos[0].img %>') no-repeat scroll center center / cover">
<a class="open-modal-image" href="<%- portfolio.photos[0].img %>"><div class="imgFigure"></div></a>
<div class="imgGal"
style="background:rgba(0, 0, 0, 0) url('<%- portfolio.photos[0].img %>') no-repeat scroll center center / cover">
<a class="open-modal-image" href="<%- portfolio.photos[0].img %>">
<div class="imgFigure"></div>
</a>
</div>
</div>
<div class="insetCol2 box-sizing disTab text-center">
<a href="<%- portfAbsUrl %>"><%- trunc({length: 50}, portfolio.name) %></a>
<a href="<%- portfAbsUrl %>"><%- trunc({length: 50}, portfolio.name)
%></a>
</div>
</div>
</script>
</div>
<div class="col-lg-9 col-lg-offset-3 -more-portfolios-btn">
<div class="linkElse">
<a href="#" onclick="loadMorePortfolios(); return false" class="showElse">показать еще</a>
<a href="#" onclick="loadMorePortfolios(); return false" class="showElse">показать
еще</a>
</div>
</div>
</div>
<div id="tab12" class="tab-pane fade">
<div class="galleryWork2 disTab -work-sells-container">
<script type="text/x-template" class="-work-sell-item-templ">
<div class="col-lg-4">
<div class="insetCol box-sizing disTab">
<div class="imgGal" style="background:rgba(0, 0, 0, 0) url('<%- workSell.photos[0].img %>') no-repeat scroll center center / cover ;">
<a class="open-modal-image" href="<%- workSell.photos[0].img %>"><div class="imgFigure"></div></a>
<div class="imgGal"
style="background:rgba(0, 0, 0, 0) url('<%- workSell.photos[0].img %>') no-repeat scroll center center / cover ;">
<a class="open-modal-image" href="<%- workSell.photos[0].img %>">
<div class="imgFigure"></div>
</a>
</div>
<div class="cenaImg box-sizing">
<div class="cenaImgInset">
<%- workSell.budget %> <i class="fa fa-rub"></i>
@ -206,19 +222,21 @@
</div>
</div>
<div class="insetCol2 box-sizing disTab">
<a href="<%- workSellAbsUrl %>"><%- trunc({length: 50}, workSell.name) %></a>
<a href="<%- workSellAbsUrl %>"><%- trunc({length: 50}, workSell.name)
%></a>
</div>
</div>
</script>
</div>
<div class="col-lg-9 col-lg-offset-3 -more-work-sells-btn">
<div class="linkElse">
<a href="#" onclick="loadMoreWorkSells(); return false" class="showElse">показать еще</a>
<a href="#" onclick="loadMoreWorkSells(); return false" class="showElse">показать
еще</a>
</div>
</div>
</div>
<div id="tab13" class="tab-pane fade">
{% for review in reviews %}
<div class="new-comm-44">
@ -226,15 +244,23 @@
<p class="nameComm">
<a href="#">{{ review.get_sender }}</a>
</p>
{% if review.project.deal_type == 'secure_deal' %}
<span class="dateComm44">Безопасная сделка</span>
{% endif %}
<div class="stars box-sizing">
<a href="#">положительный отзыв</a>
<a href="#">
{% if review.type == 'positive' %}
Положительный отзыв
{% elif review.type == 'negative' %}
Отрицательный отзыв
{% else %}
Нейтральный отзыв
{% endif %}
</a>
</div>
<p class="textComm44">
{{ review.text|safe }}
</p>
@ -250,7 +276,7 @@
</div>
</div>
{% endif %}
{% include 'partials/footer.html' %}
</div>
</div>
@ -258,116 +284,117 @@
{% block js_block %}
<script>
(function() {
(function () {
// Pagination ---------------------------------------------------
var $portfoliosContainer = $('.-portfolios-container').first()
var portfolioItemTempl = _.template($portfoliosContainer.find('.-portfolio-item-templ').first().html())
var $morePortfBtn = $('.-more-portfolios-btn').first()
var portfAbsUrl = '/projects/portfolio/'
var $workSellsContainer = $('.-work-sells-container').first()
var workSellItemTempl = _.template($workSellsContainer.find('.-work-sell-item-templ').first().html())
var $moreWorkSellsfBtn = $('.-more-work-sells-btn').first()
var workSellAbsUrl = '/work_sell/'
var contractorId = {{ contractor.pk }}
var teamId = {{ contractor.team.pk }}
var contractorId =
{{ contractor.pk }}
var teamId =
{{ contractor.team.pk }}
var contractorIds = [contractorId]
var portfUrl = new URI('/api/portfolios/')
var workSellUrl = new URI('/api/work-sells/')
var pageSize = {% if TESTING %}99999{% else %}9{% endif %}
$.get('/api/teams/' + teamId + '/').then(function(res) {
Array.prototype.push.apply(contractorIds, _.map(function(contractor) {
return contractor.id
}, res.contractors))
})
.then(loadMorePortfolios)
.then(loadMoreWorkSells)
$.get('/api/teams/' + teamId + '/').then(function (res) {
Array.prototype.push.apply(contractorIds, _.map(function (contractor) {
return contractor.id
}, res.contractors))
})
.then(loadMorePortfolios)
.then(loadMoreWorkSells)
function loadMorePortfolios() {
var query = portfUrl.query(true)
portfUrl.setQuery('user__id__in', _.join(',', contractorIds))
portfUrl.setQuery('page_size', pageSize)
portfUrl.setQuery('page', query.page ? Number(query.page) + 1 : 1)
$.get(portfUrl.href())
.then(function(res) {
_.each(function(portfolio) {
$portfoliosContainer.append(portfolioItemTempl({
portfolio: portfolio,
portfAbsUrl: portfAbsUrl + portfolio.id + '/',
trunc: _.truncate,
}))
}, res.results)
if (!res.next)
$morePortfBtn.css('display', 'none')
})
.then(function() {
$('.open-modal-image').magnificPopup({type: 'image'})
})
.then(function (res) {
_.each(function (portfolio) {
$portfoliosContainer.append(portfolioItemTempl({
portfolio: portfolio,
portfAbsUrl: portfAbsUrl + portfolio.id + '/',
trunc: _.truncate,
}))
}, res.results)
if (!res.next)
$morePortfBtn.css('display', 'none')
})
.then(function () {
$('.open-modal-image').magnificPopup({type: 'image'})
})
}
function loadMoreWorkSells() {
var query = workSellUrl.query(true)
workSellUrl.setQuery('contractor', contractorId)
workSellUrl.setQuery('page_size', pageSize)
workSellUrl.setQuery('page', query.page ? Number(query.page) + 1 : 1)
$.get(workSellUrl.href()).then(function(res) {
_.each(function(ws) {
$.get(workSellUrl.href()).then(function (res) {
_.each(function (ws) {
$workSellsContainer.append(workSellItemTempl({
workSell: ws,
workSellAbsUrl: workSellAbsUrl + ws.id + '/',
trunc: _.truncate,
}))
}, res.results)
if (!res.next)
$moreWorkSellsfBtn.css('display', 'none')
})
}
window.loadMorePortfolios = loadMorePortfolios
window.loadMoreWorkSells = loadMoreWorkSells
// Add new team member --------------------------------------------
{% if contractor.has_team %}
var $contractorSelect = $('.-contractor-select').first()
var $addTeamMemberModal = $('#addTeamMemberModal')
var contractorExcludeIds = JSON.parse({{ team_invitation_exclude_contractor_ids|json }})
initContractorSelect($contractorSelect, contractorExcludeIds)
$addTeamMemberModal.find('.-action-button').first().on('click', function($evt) {
$addTeamMemberModal.find('.-action-button').first().on('click', function ($evt) {
$addTeamMemberModal.modal('hide')
var contractor2Id = $contractorSelect.select2('val')
var createTeamInvitationUrl = format('/users/create-team-invitation/%s/', contractor2Id)
$.post(createTeamInvitationUrl).then(function(res) {
$.post(createTeamInvitationUrl).then(function (res) {
if (res.status === 'success') {
socketMain.add_message({
format_type: 'add_message_contact',
data: {
sender_id: '{{ contractor.pk }}',
recipent_id: String(contractor2Id),
chat_message: 'Приглашаю в группу http://{{ request.get_host }}{% url 'users:accept-team-invitation' owner_id=contractor.pk %}',
},
})
$.jGrowl('Приглашение успешно отправлено')
} else {
$.jGrowl('Произошла ошибка при отправке приглашения')

@ -18,23 +18,19 @@
<span class="dateComm44">
{{ review.project.get_deal_type_display }}
</span>
<div class="stars box-sizing">
{% for star in review.stars|range %}
<span class="glyphicon glyphicon-star starAct" aria-hidden="true"></span>
{% endfor %}
{% for filler_star in 5|sub:review.stars|range %}
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
{% endfor %}
{% if review.stars < 3 %}
<a href="#" onclick="return false" style="color: red">отрицательный отзыв</a>
{% else %}
<a href="#" onclick="return false">положительный отзыв</a>
{% endif %}
<a href="#">
{% if review.type == 'positive' %}
Положительный отзыв
{% elif review.type == 'negative' %}
Отрицательный отзыв
{% else %}
Нейтральный отзыв
{% endif %}
</a>
</div>
<p class="textComm44">{{ review.text }}</p>
</div>
</div>

@ -1,17 +1,18 @@
import math
from django import template
from chat.models import NewMessage
register = template.Library()
@register.inclusion_tag('templatetags/contractor_indicator.html', takes_context=True)
def contractor_indicator(context, contractor):
fields = ['avatar','cro','first_name','gender',
fields = ['avatar', 'cro', 'first_name', 'gender',
'last_name', 'patronym', 'phone',
'skype','website','location']
'skype', 'website', 'location']
indicator_sum = 0
for f in fields:
if hasattr(contractor,f) and getattr(contractor, f):
if hasattr(contractor, f) and getattr(contractor, f):
indicator_sum += 1
current_indicator = math.ceil(math.ceil(100 / len(fields)) * indicator_sum)
@ -39,11 +40,23 @@ def count_new_message(context, user):
@register.inclusion_tag('templatetags/user_new_count_orders.html', takes_context=True)
def count_new_message_orders(context, user):
from chat.models import NewMessage
new_count = NewMessage.objects.filter(user=user, message__order__in=user.orders.all(), message__team__isnull=True).count()
if user.team:
new_count_team = NewMessage.objects.filter(user=user, message__order__in=user.team.orders.all(), message__team__isnull=True).count()
new_count +=new_count_team
new_count = NewMessage.objects.filter(user=user, message__order__in=user.orders.all(),
message__team__isnull=True).count()
try:
team = user.team
except:
team = None
if team:
new_count_team = NewMessage.objects.filter(user=user, message__order__in=user.team.orders.all(),
message__team__isnull=True).count()
new_count += new_count_team
return {
'new_count': new_count,
}
@register.simple_tag
def get_new_count_message(team_pk,user=None):
count = NewMessage.objects.filter(user=user, message__team=team_pk, message__order__isnull=True).count()
return count

Loading…
Cancel
Save