diff --git a/archilance/util.py b/archilance/util.py index b7feef0..b919d9f 100644 --- a/archilance/util.py +++ b/archilance/util.py @@ -1,9 +1,11 @@ +from django.core import validators from django.shortcuts import _get_queryset from django.utils import timezone from pprint import pprint, pformat import natsort import pydash as _; _.map = _.map_; _.filter = _.filter_ import random +import string def take(coll, n): @@ -89,3 +91,21 @@ def decap(s): raise TypeError('String expected') return s[0].lower() + s[1:] if len(s) > 0 else s + + +def random_ident(length=8): + return ''.join( + [random.choice(string.ascii_lowercase)] + + _.times(lambda x_: random.choice(string.ascii_letters + string.digits), length-1) + ) + + +def validate_phone(text): + text = text.replace(' ', '').replace('-', '') + + validate = validators.RegexValidator( + regex=r'^((\+7|8)(\(\d{3}\)|(\d{3}))\d{7})$', + message='Неверный номер телефона. Формат: +71112223344', + ) + + validate(text) diff --git a/assets/css/main.css b/assets/css/main.css index 0bf5b8c..23624e2 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -1041,7 +1041,7 @@ footer:after { background-position: 0 -26px; } -.rightPro ul li:last-child:before { +.rightPro ul li:nth-child(3):before { height: 22px; background: url('../img/cenaList.png') no-repeat left; background-size: cover; @@ -5639,13 +5639,13 @@ input[type="radio"]:checked + span { right: 3px; top: 16px; } -#portfolio-add-form .textAreaBlock2 p{ +#portfolio-add-form .textAreaBlock2 p, #worksell-add-form .textAreaBlock2 p{ font-size: 15px; font-family: 'Arial-MT-Regular', sans-serif; font-weight: bold; color: #2c2c2c; } -#portfolio-add-form #text-new{ +#portfolio-add-form #text-new, #worksell-add-form #text-new{ margin-top: 0; } .textAreaBlock2 input{ @@ -5657,10 +5657,10 @@ input[type="radio"]:checked + span { color: #9c9c9c; font-family: 'Arial-MT-Regular', sans-serif; } -#portfolio-add-form .polsF1 .btn{ +#portfolio-add-form .polsF1 .btn, #worksell-add-form .polsF1 .btn{ border:1px solid #c2c2c2; } -#portfolio-add-form .bootstrap-select{ +#portfolio-add-form .bootstrap-select, #worksell-add-form .bootstrap-select{ width: 100% !important; } .textAreaBlock2 .add_file_to_port{ @@ -5750,7 +5750,7 @@ input[type="radio"]:checked + span { margin: 4px 0 4px 0; } .birth_edit_dat .bootstrap-select:not([class*="col-"]):not([class*="form-control"]):not(.input-group-btn){ - width: 190px; + width: 190px !important; } .btn-submit-link{ color: #333 !important; @@ -5758,4 +5758,65 @@ input[type="radio"]:checked + span { border-color: #ccc !important; margin-bottom: 0 !important } +.infoProfile .dropdown-menu{ + height: 316px; +} +.form-regestration{ + padding: 20px 0 40px 0; +} +#resume-text-edit .modal-title, #fullfill-balance .modal-title, #withdraw-money .modal-title{ + font-family: Arial, Verdana, Helvetica, sans-serif; + font-size: 16px; + text-transform: uppercase; + color: #2c2c2c; + font-weight: bold; +} +#withdraw-money .modal-body, #fullfill-balance .modal-body{ + width: 100%; + display: table; +} +#withdraw-money .modal-body p, #fullfill-balance .modal-body p{ + color: #2c2c2c; + font-family: Arial, Verdana, Helvetica, sans-serif; + font-weight: bold; +} +#withdraw-money .modal-footer .btn, #fullfill-balance .modal-footer .btn{ + padding: 10px 34px; + border-radius: 40px; + font-family: 'pfdintextcomppro-regular', sans-serif; + font-size: 16px; + text-transform: uppercase; + letter-spacing: 2px; +} +:focus{ + outline: none !important; +} +#withdraw-money .modal-footer .btn.btn-primary, #fullfill-balance .modal-footer .btn.btn-primary{ + background-color: #000; +} +.modal-footer{ + border-top: none; +} +#resume-text-edit .btn-submit-link{ + float: right; + margin-top: 15px; +} +#resume-text-edit .searchF1{ + padding: 0; +} +.list-summ-block{ + min-height: 195px; +} +.select2-dropdown-open > a.select2-choice{ + border: 1px solid #ff0029 !important; +} +.select2-display-none { + border: 1px solid #ff0029 !important; +} +.bootstrap-select.btn-group .dropdown-menu li a span.text { + font-size: 14px; + display: block; + text-transform: initial; + letter-spacing: normal; +} /*end_new*/ \ No newline at end of file diff --git a/assets/js/chat.js b/assets/js/chat.js index 5430d7f..d8e0883 100644 --- a/assets/js/chat.js +++ b/assets/js/chat.js @@ -1,6 +1,6 @@ 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 () { diff --git a/assets/js/main.js b/assets/js/main.js index 29edb85..b623a9a 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -3,9 +3,14 @@ $(document).ready(function(){ $('.messd:first').addClass('mesAct'); $('.orderBlock:first').addClass('orAct'); - if(!$.cookie('slideResVisible')) { + if ($.cookie('slideResVisible')) { + $('.slideRes').slideDown(300); + $('.resButtonF1').css('transform','rotate(0deg)'); + $('#extraFields').val('on') + } else { $('.slideRes').slideUp(300); $('.resButtonF1').css('transform','rotate(180deg)'); + $('#extraFields').val('') } $('.resButtonF1').click(function(e){ @@ -15,10 +20,12 @@ $(document).ready(function(){ $('.slideRes').slideUp(300); $(this).css('transform','rotate(180deg)'); $.cookie('slideResVisible', '', {expires: new Date(new Date().getTime() + 300000)}) // 5 minutes + $('#extraFields').val('') } else { $('.slideRes').slideDown(300); $(this).css('transform','rotate(0deg)'); $.cookie('slideResVisible', 'on', {expires: new Date(new Date().getTime() + 300000)}) + $('#extraFields').val('on') } }); diff --git a/chat/templates/chat_contractor.html b/chat/templates/chat_contractor.html index af98f98..c76cb06 100644 --- a/chat/templates/chat_contractor.html +++ b/chat/templates/chat_contractor.html @@ -47,7 +47,13 @@ {% endthumbnail %}
-
+ {% if contact.is_contractor %} + {% url "users:contractor-profile" pk=contact.pk as contact_url %} + {% else %} + {% url "users:customer-profile-open-projects" pk=contact.pk as contact_url %} + {% endif %} + + Контакты diff --git a/chat/templates/chat_customer.html b/chat/templates/chat_customer.html index 70e5022..c447189 100644 --- a/chat/templates/chat_customer.html +++ b/chat/templates/chat_customer.html @@ -41,7 +41,12 @@ {% endif %}-
+ {% if contact.is_contractor %} + {% url "users:contractor-profile" pk=contact.pk as contact_url %} + {% else %} + {% url "users:customer-profile-open-projects" pk=contact.pk as contact_url %} + {% endif %} + - + - - - - +{{ object }}
+ + {% if object.get_prev %} +{{ object.budget }}
#} + {#Описание:
++ {{ object.text }} +
+Похожие работы
#} +{#{{ project.name }}
-{{ project.name }}
+{{ project.budget|intcomma }}
+ {{ project.customer.get_full_name }} [{{ project.customer.username }}] +
+ + + {% if not request.user.is_contractor %} +Есть допуск СРО
+ {% endif %} + +
{{ project.text }}
-Опубликован: {{ answer.created|date:'M d, Y' }}
Исполнители
Новые исполнители
-
+ Новые исполнители
+
+ {% endif %}
+ + {% if answer.author|class_name == 'User' %} + {{ answer.author.get_full_name }} [{{ answer.author.username }}] + {% elif answer.author|class_name == 'Team' %} + {{ answer.author.name }} + {% endif %} +
+ + + + {% if answer.author|class_name == 'User' %} + {% if answer.author.contractor_status == 'free' %} +- {% if answer.author|class_name == 'User' %} - {{ answer.author.get_full_name }} [{{ answer.author.username }}] - {% elif answer.author|class_name == 'Team' %} - {{ answer.author.name }} - {% endif %} -
- - - - {% if answer.author|class_name == 'User' %} - {% if answer.author.contractor_status == 'free' %} -Есть допуск СРО
+ +Есть допуск СРО
+Есть допуск СРО
+Есть допуск СРО
++ Цена: + {{ answer.budget|intcomma }} + +
++ Срок: {{ answer.term }} {{ answer.get_currency_display }} {{ answer.get_term_type_display|decap }} +
+Опубликован: {{ answer.created|date:'M d, Y' }}
- Цена: - {{ answer.budget|intcomma }} - -
-- Срок: {{ answer.term }} {{ answer.get_currency_display }} {{ answer.get_term_type_display|decap }} -
-Опубликован: {{ answer.created|date:'M d, Y' }}
-+ {{ project.customer.get_full_name }} +
+ {% else %} ++ {% if answer.author|class_name == 'User' %} + {{ answer.author.get_full_name }} + {% elif answer.author|class_name == 'Team' %} + {{ answer.author.name }} + {% endif %} +
+ {% endif %} + + + {{ message.created }} + + ++ {{ message.text|linebreaksbr }} +
+- {{ project.customer.get_full_name }} -
- {% else %} -- {% if answer.author|class_name == 'User' %} - {{ answer.author.get_full_name }} - {% elif answer.author|class_name == 'Team' %} - {{ answer.author.name }} - {% endif %} -
+ {% endfor %} +Кандидаты
+
+ {% endif %}
+ + {% if answer.author|class_name == 'User' %} + {{ answer.author.get_full_name }} [{{ answer.author.username }}] + {% elif answer.author|class_name == 'Team' %} + {{ answer.author.name }} {% endif %} +
+ + + + {% if answer.author|class_name == 'User' %} + {% if answer.author.contractor_status == 'free' %} +Есть допуск СРО
+- {{ message.text }} -
-Есть допуск СРО
+Отказы
+ {% endif %}
+ + {% if answer.author|class_name == 'User' %} + {{ answer.author.get_full_name }} [{{ answer.author.username }}] + {% elif answer.author|class_name == 'Team' %} + {{ answer.author.name }} + {% endif %} +
+ + + + {% if answer.author|class_name == 'User' %} + {% if answer.author.contractor_status == 'free' %} +Есть допуск СРО
+Есть допуск СРО
++ Цена: + {{ answer.budget|intcomma }} + +
++ Срок: {{ answer.term }} {{ answer.get_currency_display }} {{ answer.get_term_type_display|decap }} +
+Опубликован: {{ answer.created|date:'M d, Y' }}
++ {{ project.customer.get_full_name }} +
+ {% else %} ++ {% if answer.author|class_name == 'User' %} + {{ answer.author.get_full_name }} + {% elif answer.author|class_name == 'Team' %} + {{ answer.author.name }} + {% endif %} +
+ {% endif %} + + + {{ message.created }} + + ++ {{ message.text|linebreaksbr }} +
+Биржа проектов
-Биржа проектов
+Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
-
+
context.update({'form': form})
return render(request, self.template_name, context)
else:
@@ -132,41 +133,41 @@ class ProjectDetailWithAnswerView(BaseMixin, View):
class ProjectAnswerCreateMessageView(BaseMixin, View):
form_class = ProjectAnswerMessageForm
-
+
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated():
return super().dispatch(request, *args, **kwargs)
else:
return HttpResponseForbidden('403 Forbidden')
-
+
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, request=request)
-
+
if form.is_valid():
message = form.save(commit=False)
-
+
if request.user.is_contractor():
answer = get_object_or_404(Answer, pk=kwargs.get('pk'))
-
+
message.answer = answer
message.is_sender_customer = False
-
+
if isinstance(answer.author, User) and answer.author == request.user:
message.contractor_or_team = request.user
elif isinstance(answer.author, Team):
try: team = request.user.team
except Team.DoesNotExist: team = None
-
+
if team and answer.author == team:
message.contractor_or_team = team
elif request.user.is_customer():
answer = get_object_or_404(Answer, pk=kwargs.get('pk')) # TODO: Perform additional checks
message.answer = answer
message.is_sender_customer = True
-
+
message.save()
form.save_m2m()
-
+
messages.info(request, 'Сообщение успешно размещено')
else:
if form.errors:
@@ -174,49 +175,30 @@ class ProjectAnswerCreateMessageView(BaseMixin, View):
'Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
-
+
redirect_to = request.POST.get('next')
return redirect(redirect_to)
-class ContractorRejectProjectAnswerView(BaseMixin, View):
+class RejectProjectAnswerView(BaseMixin, View):
def dispatch(self, request, *args, **kwargs):
- if request.user.is_authenticated() and request.user.is_contractor():
+ if request.user.is_authenticated():
return super().dispatch(request, *args, **kwargs)
else:
- return HttpResponseForbidden('403 Forbidden')
-
+ raise PermissionDenied
+
def post(self, request, *args, **kwargs):
- answer = get_object_or_404(request.user.contractor_answers, pk=kwargs.get('pk'))
-
- answer.delete()
-
- messages.info(request, 'Вы успешно отказались от проекта')
-
- redirect_to = request.POST.get('next')
- return redirect(redirect_to)
+ if request.user.is_contractor():
+ answer = get_object_or_404(request.user.contractor_answers, pk=kwargs.get('pk'))
+ elif request.user.is_customer():
+ project = get_object_or_404(request.user.projects, answers__pk=kwargs.get('pk'))
+ answer = get_object_or_404(project.answers, pk=kwargs.get('pk'))
+
+ answer.rejected = True
+ answer.save()
+ messages.info(request, 'Успешный отказ от проекта')
-class CustomerRejectProjectAnswerView(BaseMixin, View):
- def dispatch(self, request, *args, **kwargs):
- if request.user.is_authenticated() and request.user.is_customer():
- return super().dispatch(request, *args, **kwargs)
- else:
- return HttpResponseForbidden('403 Forbidden')
-
- def post(self, request, *args, **kwargs):
- answer_id = kwargs.get('pk')
-
- project = get_object_or_404(request.user.projects, answers__pk=answer_id)
- answer = get_object_or_404(project.answers, pk=answer_id)
-
- answer.delete()
-
- project.rejected_answers_count = F('rejected_answers_count') + 1
- project.save()
-
- messages.info(request, 'Вы успешно отказали пользователю в проекте')
-
redirect_to = request.POST.get('next')
return redirect(redirect_to)
@@ -225,107 +207,107 @@ class ProjectFilterView(BaseMixin, View):
template_name = 'project_filter.html'
form_class = ProjectFilterForm
realty_form = ProjectFilterRealtyForm
-
+
def get(self, request, *args, **kwargs):
form = self.form_class(request.GET, request=request)
realty_form = self.realty_form(request.GET, request=request, prefix='realty_form')
context = self.get_context_data(**_.merge({}, request.GET, kwargs))
-
+
projects = Project.objects
-
+
if form.is_valid() and realty_form.is_valid():
ord = None
-
+
keywords = form.cleaned_data.get('keywords')
cro = form.cleaned_data.get('cro')
work_type = form.cleaned_data.get('work_type')
specialization = form.cleaned_data.get('specialization')
-
+
building_classification = realty_form.cleaned_data.get('building_classification')
construction_type = realty_form.cleaned_data.get('construction_type')
location = realty_form.cleaned_data.get('location')
-
+
if keywords:
keywords = tuple(filter(None, re.split(r'\s|,|;', keywords)))
-
+
for k in keywords:
projects = projects.filter(Q(name__icontains=k) | Q(text__icontains=k))
-
+
if cro:
projects = projects.filter(cro=cro)
-
+
if work_type:
projects = projects.filter(work_type=work_type)
-
+
if specialization:
projects = projects.filter(
specialization__lft__gte=specialization.lft,
specialization__rght__lte=specialization.rght,
)
-
+
if building_classification:
projects = projects.filter(realty__building_classification=building_classification)
-
+
if construction_type:
projects = projects.filter(realty__construction_type=construction_type)
-
+
if location:
projects = projects.filter(
realty__location__lft__gte=location.lft,
realty__location__rght__lte=location.rght,
)
-
+
order_by = form.cleaned_data.get('order_by')
last_order_by = form.cleaned_data.get('last_order_by')
reverse_order = form.cleaned_data.get('reverse_order')
-
+
if order_by:
reverse_order = not reverse_order if order_by == last_order_by else False
ord = order_by
last_order_by = ord
elif last_order_by:
ord = last_order_by
-
+
manual_sort = None
-
+
if ord and ord == 'views':
projects = natsort.natsorted(projects.all(), key=lambda p: p.hit_count.hits, reverse=reverse_order)
manual_sort = True
elif ord:
projects = projects.order_by('-%s' % ord if reverse_order else ord)
-
+
context.update({
'last_order_by': last_order_by,
'reverse_order': reverse_order,
})
-
+
project_count = len(projects) if manual_sort else projects.count()
display_msg = 'Найдено %s проектов' % project_count if project_count > 0 else 'Ничего не найдено'
else:
display_msg = 'Пожалуйста, введите корректные данные'
-
+
if form.errors:
messages.info(request, (
'Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
-
+
if realty_form and realty_form.errors:
messages.info(request, (
'Произошла ошибка (realty_form)
' '{realty_form}'
).format(realty_form=pformat(realty_form.errors)))
-
+
paginator = Paginator(projects if manual_sort else projects.all(), settings.PAGE_SIZE)
page = request.GET.get('page')
-
+
try:
projects = paginator.page(page)
except PageNotAnInteger:
projects = paginator.page(1)
except EmptyPage:
projects = paginator.page(paginator.num_pages)
-
+
context.update({
'form': form,
'realty_form': realty_form,
@@ -334,7 +316,7 @@ class ProjectFilterView(BaseMixin, View):
'page_obj': projects,
'display_msg': display_msg,
})
-
+
return render(request, self.template_name, context)
@@ -342,33 +324,33 @@ class CustomerProjectCreateView(BaseMixin, View):
form_class = CustomerProjectEditForm
realty_form = RealtyForm
template_name = 'customer_project_create.html'
-
+
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_customer():
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
-
+
def get(self, request, *args, **kwargs):
form = self.form_class(request=request)
realty_form = self.realty_form(request=request, prefix='realty_form')
-
+
context = self.get_context_data(**_.merge({}, request.GET, kwargs))
context.update({'form': form, 'realty_form': realty_form})
-
+
return render(request, self.template_name, context)
-
+
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, request=request) # Passing `request.FILES` seems unnecessary here. Files are added manually below
-
+
form.is_valid()
realty = form.cleaned_data.get('realty')
-
+
if realty:
realty_form = self.realty_form(request.POST, instance=realty, request=request, prefix='realty_form')
else:
realty_form = self.realty_form(request.POST, request=request, prefix='realty_form')
-
+
if form.is_valid() and realty_form.is_valid():
project = form.save(commit=False)
project.customer = request.user
@@ -378,12 +360,12 @@ class CustomerProjectCreateView(BaseMixin, View):
secure = False
if 'secure_deal' in project.deal_type:
secure = True
-
+
Order.objects.create(project=project, secure=secure)
for file in request.FILES.getlist('new_files'):
ProjectFile.objects.create(file=file, project=project)
-
+
if realty:
realty_form.save()
else:
@@ -391,10 +373,10 @@ class CustomerProjectCreateView(BaseMixin, View):
realty.user = request.user
realty.save()
realty_form.save_m2m()
-
+
project.realty = realty # Connect a realty with a project
project.save()
-
+
messages.info(request, 'Проект успешно создан')
redirect_to = reverse('projects:detail', kwargs={'pk': project.pk})
return redirect(redirect_to)
@@ -404,13 +386,13 @@ class CustomerProjectCreateView(BaseMixin, View):
'Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
-
+
if realty_form and realty_form.errors:
messages.info(request, (
'Произошла ошибка (realty_form)
' '{realty_form}'
).format(realty_form=pformat(realty_form.errors)))
-
+
context = self.get_context_data(**kwargs)
context.update({'form': form, 'realty_form': realty_form})
return render(request, self.template_name, context)
@@ -420,52 +402,52 @@ class CustomerProjectEditView(BaseMixin, View):
form_class = CustomerProjectEditForm
realty_form = RealtyForm
template_name = 'customer_project_edit.html'
-
+
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_customer():
return super().dispatch(request, *args, **kwargs)
else:
- return HttpResponseForbidden('403 Forbidden')
+ raise PermissionDenied
def get(self, request, *args, **kwargs):
project = get_object_or_404(request.user.projects, pk=kwargs.get('pk'))
form = self.form_class(instance=project, request=request)
-
+
realty = project.realty
-
+
if realty:
realty_form = self.realty_form(instance=project.realty, request=request, prefix='realty_form')
else:
realty_form = self.realty_form(request=request, prefix='realty_form')
-
+
context = self.get_context_data(**_.merge({}, request.GET, kwargs))
context.update({'form': form, 'realty_form': realty_form})
-
+
return render(request, self.template_name, context)
-
+
def post(self, request, *args, **kwargs):
project = get_object_or_404(request.user.projects, pk=kwargs.get('pk'))
form = self.form_class(request.POST, request.FILES, request=request, instance=project)
-
+
form.is_valid()
realty = form.cleaned_data.get('realty')
-
+
if realty:
realty_form = self.realty_form(request.POST, instance=realty, request=request, prefix='realty_form')
else:
realty_form = self.realty_form(request.POST, request=request, prefix='realty_form')
-
+
if form.is_valid() and realty_form.is_valid():
project = form.save(commit=False)
project.customer = request.user
project.files = form.cleaned_data.get('files') # TODO: Should we somehow get rid of this explicit assignment?
project.save()
form.save_m2m()
-
+
for file in request.FILES.getlist('new_files'):
proj_file = ProjectFile.objects.create(file=file, project=project)
proj_file.save()
-
+
if realty:
realty_form.save()
else:
@@ -473,10 +455,10 @@ class CustomerProjectEditView(BaseMixin, View):
realty.user = request.user
realty.save()
realty_form.save_m2m()
-
+
project.realty = realty # Connect a realty with a project
project.save()
-
+
messages.info(request, 'Проект успешно отредактирован')
redirect_to = request.POST.get('next')
return redirect(redirect_to)
@@ -486,13 +468,13 @@ class CustomerProjectEditView(BaseMixin, View):
'Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
-
+
if realty_form and realty_form.errors:
messages.info(request, (
'Произошла ошибка (realty_form)
' '{realty_form}'
).format(realty_form=pformat(realty_form.errors)))
-
+
context = self.get_context_data(**kwargs)
context.update({'form': form, 'realty_form': realty_form})
return render(request, self.template_name, context)
@@ -528,41 +510,41 @@ class ContractorPortfolioTrashView(View):
class CustomerProjectTrashView(View):
form_class = CustomerProjectTrashForm
-
+
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_customer():
return super().dispatch(request, *args, **kwargs)
else:
return HttpResponseForbidden('403 Forbidden')
-
+
def post(self, req, *args, **kwargs):
form = self.form_class(_.merge({}, req.POST, kwargs), req=req)
-
+
if form.is_valid():
project = form.cleaned_data.get('pk')
project.state = 'trashed'
project.save()
-
+
messages.info(req, 'Проект перемещён в корзину')
else:
messages.info(req, 'Произошла ошибка: {msg}'.format(msg=pformat(form.errors)))
-
+
redirect_to = req.POST.get('next')
return redirect(redirect_to)
class CustomerProjectRestoreView(View):
form_class = CustomerProjectRestoreForm
-
+
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_customer():
return super().dispatch(request, *args, **kwargs)
else:
return HttpResponseForbidden('403 Forbidden')
-
+
def post(self, req, *args, **kwargs):
form = self.form_class(_.merge({}, req.POST, kwargs), req=req)
-
+
if form.is_valid():
project = form.cleaned_data.get('pk')
project.state = 'active'
@@ -571,23 +553,23 @@ class CustomerProjectRestoreView(View):
messages.info(req, 'Проект восстановлен из корзины')
else:
messages.info(req, 'Произошла ошибка: {msg}'.format(msg=pformat(form.errors)))
-
+
redirect_to = req.POST.get('next')
return redirect(redirect_to)
class CustomerProjectDeleteView(View):
form_class = CustomerProjectDeleteForm
-
+
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_customer():
return super().dispatch(request, *args, **kwargs)
else:
return HttpResponseForbidden('403 Forbidden')
-
+
def post(self, req, *args, **kwargs):
form = self.form_class(_.merge({}, req.POST, kwargs), req=req)
-
+
if form.is_valid():
project = form.cleaned_data.get('pk')
project.state = 'deleted'
@@ -595,7 +577,7 @@ class CustomerProjectDeleteView(View):
messages.info(req, 'Проект удалён навсегда')
else:
messages.info(req, 'Произошла ошибка: {msg}'.format(msg=pformat(form.errors)))
-
+
redirect_to = req.POST.get('next')
return redirect(redirect_to)
@@ -672,7 +654,7 @@ class OfferOrderView(View):
return HttpResponseRedirect(redirect_url)
-def contractor_portfolio_create(request):
+def contractor_portfolio_create(request): # TODO: pekopt: shit. rewrite using generic
if request.is_ajax():
form = PortfolioForm(data=request.POST)
# import code; code.interact(local=dict(globals(), **locals()))
@@ -729,4 +711,11 @@ class PortfolioDelete(DeleteView):
model = Portfolio
success_url = reverse_lazy('users:contractor-profile')
+
+class PortfolioDetail(DetailView):
+ model = Portfolio
+ template_name = 'portfolio_detail.html'
+
+
+
# import code; code.interact(local=dict(globals(), **locals()))
diff --git a/ratings/templates/templatetags/ratings_widget.html b/ratings/templates/templatetags/ratings_widget.html
index 9a9305d..e756f47 100644
--- a/ratings/templates/templatetags/ratings_widget.html
+++ b/ratings/templates/templatetags/ratings_widget.html
@@ -1,12 +1,12 @@
#} -{# Специализации:#} -{#
#} -{#Специализации:
{% for spec in specializations %} @@ -40,5 +5,7 @@ {{ spec.specialization.name }} {{ spec.position }}-й{{ p.name }}
+ {{ p }}Работ в портфолио пока нет
+{{ ws }}
- + {{ ws }}Готовых работ пока нет
+- Иванов Петр Иванович + {{ review.get_sender }}
- - Безопасная сделка - + {% if review.project.deal_type == 'secure_deal' %} + + Безопасная сделка + + {% endif %}- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod bibendum - + {{ review.text|safe }}
Отзывов пока нет
+{{ p.name }}
- + {{ p }} {% if request.user.pk == contractor.pk %}Работ в портфолио пока нет
+{{ ws }}
- + {{ ws }} {% if request.user.pk == contractor.pk %} + {% empty %} +Отзывов пока нет
+Дублировать проект в готовые работы
Бюджет{{ worksell_form.budget.errors.as_text }}
Срок выполнения{{ worksell_form.budget.errors.as_text }}
Вид строительства
+Вид строительства
+Классификация здания
+{msg}'.format(msg=pformat(form.errors))) # Debug
class TmpPaymentAvisoView(View):
form_class = TmpPaymentAvisoForm
-
+
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
-
+
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
-
+
if form.is_valid():
transaction = form.cleaned_data.get('transaction_id')
transaction.complete = True
@@ -173,7 +170,7 @@ class TmpPaymentAvisoView(View):
invoice_id=form.cleaned_data.get('invoiceId'),
shop_id=form.cleaned_data.get('shopId'),
)
-
+
return HttpResponse(res, content_type='application/xml')
else:
res = """
@@ -183,6 +180,6 @@ class TmpPaymentAvisoView(View):
message="Payment aviso, validation error"
techMessage="Payment aviso, validation error"/>
""".format(date=timezone.now().isoformat())
-
+
return HttpResponse(res, content_type='application/xml')
# return HttpResponse('{msg}'.format(msg=pformat(form.errors))) # Debug
diff --git a/work_sell/templates/worksell_detail.html b/work_sell/templates/worksell_detail.html
index b6d7e05..e43e153 100644
--- a/work_sell/templates/worksell_detail.html
+++ b/work_sell/templates/worksell_detail.html
@@ -48,20 +48,21 @@
Описание:
{{ object.text }} -
+Похожие работы
diff --git a/work_sell/templates/worksells_list.html b/work_sell/templates/worksells_list.html index 708b3ad..ba8f9c8 100644 --- a/work_sell/templates/worksells_list.html +++ b/work_sell/templates/worksells_list.html @@ -140,7 +140,7 @@ {% for work in work_sells %}{{ work }}
- + {{ work }} {% if request.user.pk == work.contractor.pk %}