diff --git a/assets/css/main.css b/assets/css/main.css index 46f23b6..1dd75f8 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -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; } diff --git a/assets/js/chat.js b/assets/js/chat.js index ad3b264..b05cd8a 100644 --- a/assets/js/chat.js +++ b/assets/js/chat.js @@ -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 = '' + file.name + '

'; + var htmlImg = '
' + file.name + '' + + '
'; 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 = '' + file.name + ''; + var htmlImg = '
' + file.name + '' + + '
'; 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); + } + }); + }); }); diff --git a/assets/js/chat_contractor.js b/assets/js/chat_contractor.js index 5e86dc0..5ba73f1 100644 --- a/assets/js/chat_contractor.js +++ b/assets/js/chat_contractor.js @@ -48,7 +48,8 @@ $(function () { var currentValue = $("#documentSendIds").val(); currentValue += file.id + ';'; $("#documentSendIds").val(currentValue); - var htmlImg = '' + file.name + ''; + var htmlImg = '
' + file.name + '' + + '
'; 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 () { '

Этап ' + v.pos + '' + v.name + '

' + '

Результаты этапа:' + v.result + '

' + '

Срок до ' + v.term + '

' + v.cost + ' ' + - '

Cрок заказа рассчитывается с момента резервирования средств

' + + '

Cрок этапа рассчитывается с момента резервирования средств

' + '

' + statusName + '

'; }); @@ -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(); + } }); diff --git a/assets/js/chat_customer.js b/assets/js/chat_customer.js index 2132ff1..d614782 100644 --- a/assets/js/chat_customer.js +++ b/assets/js/chat_customer.js @@ -99,12 +99,12 @@ $(function () { if (stageCount == 0) { htmlInboxStage += '
' + '

Этап 1

' + - '' + - '' + + '

' + + '

' + '' + '' + - '' + - '' + + '

' + + '

' + '' + '
'; } @@ -135,12 +135,13 @@ $(function () { if (v.status == "not_agreed" || v.status == 'cancel_approve' || v.status == 'send_approve') { htmlInbox += '
' + '

Этап

' + - '' + - '' + + '

' + + '

' + + '

' + '' + '' + - '' + - '' + + '

' + + '

' + '
'; } 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 = '
' + '

Этап ' + pos + '

' + - '' + - '' + + '

' + + '

' + '' + '' + - '' + - '' + - '
'; + '

' + + '

' + + ''; 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); }); -}); +}) +; diff --git a/chat/serializers.py b/chat/serializers.py index 74da60e..31c153c 100644 --- a/chat/serializers.py +++ b/chat/serializers.py @@ -44,6 +44,7 @@ class MessageSerializer(ModelSerializer): documents = DocumentsSerializer(read_only=True, many=True) text = serializers.SerializerMethodField() + class Meta: model = Message diff --git a/chat/templates/chat_contractor.html b/chat/templates/chat_contractor.html index 83b7bc1..095e7fd 100644 --- a/chat/templates/chat_contractor.html +++ b/chat/templates/chat_contractor.html @@ -235,7 +235,6 @@ -
diff --git a/projects/models.py b/projects/models.py index 5b1ae47..3a97c1c 100644 --- a/projects/models.py +++ b/projects/models.py @@ -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'] diff --git a/projects/serializers.py b/projects/serializers.py index e17b96d..3ffbe97 100755 --- a/projects/serializers.py +++ b/projects/serializers.py @@ -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 diff --git a/templates/partials/base.html b/templates/partials/base.html index 75da402..6530a3f 100644 --- a/templates/partials/base.html +++ b/templates/partials/base.html @@ -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 () { diff --git a/users/models.py b/users/models.py index 3e95239..e21612e 100644 --- a/users/models.py +++ b/users/models.py @@ -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') diff --git a/users/templates/contractor_office.html b/users/templates/contractor_office.html index adc7804..b6785a2 100644 --- a/users/templates/contractor_office.html +++ b/users/templates/contractor_office.html @@ -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' %} - +

Личный кабинет

- + {% include 'partials/contractor_profile_tabs.html' %} - +
{% if contractor.team %} {{ contractor.team.name }} - 0 + {% get_new_count_message contractor.team request.user %} {% else %} - {% endif %}
- +
{% for team in contractor.teams.all %} {{ team.name }} + {% get_new_count_message team request.user %} {% endfor %}
- + - + {% if contractor.team %}
@@ -75,22 +78,25 @@
{% if contractor.avatar %} {% thumbnail contractor.avatar "265x264" crop="center" as im %} - profile-image + profile-image {% endthumbnail %} {% else %} - profile-image + profile-image {% endif %}
- +
- +
-

{{ contractor.team.name }}

+

{{ contractor.team.name }} +

{{ contractor.get_location }}

- + - + - +
Свободен
- +
{% specialization_team_widget contractor.team.pk %}
- +
{% ratings_team_widget contractor.team.pk %} - + {% if contractor.cro %}
@@ -148,7 +156,7 @@ {% endif %}
- +
- +
- +
- +
- +
- +
{% for review in reviews %}
{% endif %} - + {% include 'partials/footer.html' %}
@@ -258,116 +284,117 @@ {% block js_block %}