Merge branch 'PR-62'

remotes/origin/HEAD
booblegum 9 years ago
commit 5e1ac8c838
  1. 1
      assets/js/build/create_worksell.js
  2. 1863
      assets/js/build/init_portfolio_create_edit.js
  3. 275
      assets/js/build/portfolio_create_edit.js
  4. 148
      assets/js/src/init_portfolio_create_edit.js
  5. 11
      assets/js/src/portfolio_create_edit.js
  6. 1
      assets/js/src/seeds/image_upload.js
  7. 5
      assets/sass/components/custom-components.sass
  8. 28
      projects/forms.py
  9. 22
      projects/migrations/0053_portfolio_specializations.py
  10. 20
      projects/models.py
  11. 0
      projects/templates/_trash/portfolio_create.html
  12. 9
      projects/templates/customer_project_create_or_edit.html
  13. 185
      projects/templates/portfolio_create_edit.html
  14. 9
      projects/urls.py
  15. 166
      projects/views.py
  16. 3
      users/templates/contractor_profile.html
  17. 5
      webpack.config.js
  18. 10
      work_sell/templates/worksell_create.html
  19. 10
      work_sell/views.py

@ -364,6 +364,7 @@
$btn.closest('.file-upload-widget').remove();
});
} //TODO: оформить Upload'еры в виде классов
//require: <script src='{% static "my-libs.js" %}'></script>
exports.imageUploadInit = imageUploadInit;
// export {imageUploadInit, previewImg}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,275 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var _image_upload = __webpack_require__(13);
var _scroll_on_required = __webpack_require__(10);
var _ajax_send_form_data = __webpack_require__(14);
$(function () {
(0, _image_upload.imageUploadInit)();
(0, _scroll_on_required.scrollOnRequiredInit)();
window.sendFormData = _ajax_send_form_data.sendFormData;
window.scrollOnRequiredInit = _scroll_on_required.scrollOnRequiredInit;
});
/***/ },
/* 1 */,
/* 2 */,
/* 3 */,
/* 4 */,
/* 5 */,
/* 6 */
/***/ function(module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
if (cookie.substring(0, name.length + 1) == name + '=') {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function humanFileSize(bytes, si) {
var thresh = si ? 1000 : 1024;
if (Math.abs(bytes) < thresh) return bytes + ' B';
var units = si ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
var u = -1;
do {
bytes /= thresh;
++u;
} while (Math.abs(bytes) >= thresh && u < units.length - 1);
return bytes.toFixed(1) + ' ' + units[u];
}
exports.humanFileSize = humanFileSize;
exports.getCookie = getCookie;
/***/ },
/* 7 */,
/* 8 */,
/* 9 */,
/* 10 */
/***/ function(module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
function scrollOnRequiredInit() {
var $required = $('.required.error');
// console.log($required);
if (!$required.length) return;
$('html, body').animate({
scrollTop: $required.offset().top - 25
}, 1000);
}
exports.scrollOnRequiredInit = scrollOnRequiredInit;
/***/ },
/* 11 */,
/* 12 */,
/* 13 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.imageUploadInit = undefined;
var _utils = __webpack_require__(6);
// function previewImg() {
// let $fileUploadWidgets = $('.file-upload-widget');
// $.each($fileUploadWidgets, function (ind, el) {
// let $fileImg = $(el).find('img');
// let $fileInput = $(el).find('.file-upload-input');
// // console.log("$fileInput[0].files.length = ", $fileInput[0].files.length);
// // console.log("$fileInput.val() = ", $fileInput.val());
// console.log("$fileInput = ", $fileInput);
// if ($fileInput[0].files.length) {
// console.log("Попытка загрузить файл");
// let reader = new FileReader();
// reader.onload = function (e) {
// $fileImg.attr('src', e.target.result);
// };
//
// reader.readAsDataURL($fileInput[0].files[0]);
// }
// })
// }
function imageUploadInit() {
var $fileUploadContainer = $('#fileUploadContainer');
$('#fileUploadAddBtn').on('click', function ($evt) {
$fileUploadContainer.find('.file-upload-widget').last().find('.file-upload-input').click();
});
$fileUploadContainer.on('change', '.file-upload-input', function ($evt) {
var $fileInput = $(this);
var $fileUploadWidget = $fileInput.closest('.file-upload-widget');
var $fileImg = $fileUploadWidget.find('.preview');
var filePath = $fileInput.val().replace(/\\/g, '/');
var fileName = path.basename(filePath);
var fileSize = $fileInput.get(0).files && (0, _utils.humanFileSize)($fileInput.get(0).files[0].size);
console.log("file path", filePath);
console.log("file path2 ", $fileInput[0].files[0]);
if (fileName) {
$fileUploadWidget.find('.file-upload-label').text(fileName + ' ' + fileSize);
var $newFileUploadWidget = $fileUploadWidget.clone();
$newFileUploadWidget.find('.file-upload-label').text('');
$fileUploadContainer.find('.list-new-new').append($newFileUploadWidget);
$fileUploadWidget.css('display', 'block');
if ($fileImg.length) {
var reader = new FileReader();
reader.onload = function (e) {
$fileImg.css('background-image', 'url(' + e.target.result + ')');
};
reader.readAsDataURL($fileInput[0].files[0]);
}
}
});
$fileUploadContainer.on('click', '.file-upload-remove-img-btn', function ($evt) {
var $btn = $(this);
$btn.closest('.file-upload-widget').remove();
});
} //TODO: оформить Upload'еры в виде классов
//require: <script src='{% static "my-libs.js" %}'></script>
exports.imageUploadInit = imageUploadInit;
// export {imageUploadInit, previewImg}
/***/ },
/* 14 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.sendFormData = undefined;
var _utils = __webpack_require__(6);
function sendFormData(e) {
e.preventDefault();
var $target = $(e.target);
var $form = $target.closest("form");
// let formData = $form.serializeArray();
var formData = new FormData($form[0]);
$.ajax({
url: $form.attr("action"),
data: formData,
async: false,
method: $form.attr("method"),
beforeSend: function beforeSend(xhr) {
xhr.setRequestHeader("X-CSRFToken", (0, _utils.getCookie)('csrftoken'));
},
success: function success(data) {
// let data = xhr.responseJSON;
// console.log('success data -->', data);
window.location.href = data.redirect_to;
},
cache: false,
contentType: false,
processData: false,
error: function error(xhr, ajaxOptions, thrownError) {
var status = xhr.status;
$('.error').removeClass('error');
if (status == 400) {
var data = xhr.responseJSON;
$.each(data, function (key, value) {
var $header = $form.find("[name=" + key + "]").siblings('.required');
if ($header.length > 0) {
$header.addClass("error");
}
});
window.scrollOnRequiredInit();
} else {
console.log('xhr = ', xhr);
}
}
});
// $.post(url, formData).done(function (data) {
// alert(data);
// });
}
exports.sendFormData = sendFormData;
/***/ }
/******/ ]);

@ -0,0 +1,148 @@
import SelectedContainer from 'components/SelectedContainer';
import SelectedContainerCreate from 'components/SelectedContainerCreate';
import NoTreeSelect from 'components/NoTreeSelect';
import TreeSelect from 'components/TreeSelect';
// import SingleTreeSelect from 'components/SingleTreeSelect'
import SelectOrCreate from 'components/SelectOrCreate'
$(function () {
function createSpecs(url) {
// SPECIALIZATIONS
let sb_main = new TreeSelect($('#select-box-1'), {url, visible: true, required: true});
// sb_main.setHeader("Специальность");
let select_container = new SelectedContainer($('#selected-spec'),
{
obj: sb_main,
// onlyOne: true
});
sb_main.connectSelectedContainer(select_container);
let sb_1 = new TreeSelect($('#select-box-2'), {obj: sb_main});
let sb_2 = new TreeSelect($('#select-box-3'), {obj: sb_main});
let sb_3 = new TreeSelect($('#select-box-4'), {obj: sb_main});
let sb_4 = new TreeSelect($('#select-box-5'), {obj: sb_main});
// select_container.on("add", () => {
// let $container = $('#spec-value');
// $container.html($('#selected-spec').find(".selected-element").find(".name").html());
// });
sb_main.setNearbySelectBox(sb_1);
sb_1.setNearbySelectBox(sb_2, sb_main);
sb_2.setNearbySelectBox(sb_3, sb_1);
sb_3.setNearbySelectBox(sb_4, sb_2);
sb_4.setNearbySelectBox("", sb_3);
}
function createBuildingClass(url) {
// BUILDING-CLASSIFICATION
sb_build_main = new TreeSelect($('#sb-building-classification'), {url, visible: true});
sb_build_main.setHeader("Классификация здания");
let sb_build_1 = new TreeSelect($('#sb-building-sub-classification'), {obj: sb_build_main});
let select_build_container = new SelectedContainer($('#selected-building-classification'),
{
obj: sb_build_main,
onlyOne: true
});
sb_build_main.connectSelectedContainer(select_build_container);
sb_build_main.setNearbySelectBox(sb_build_1);
sb_build_1.setNearbySelectBox("", sb_build_main);
}
function createConstructionType(url) {
sb_constr_main = new NoTreeSelect($('#sb-construction-type'), {url, visible: true});
sb_constr_main.setHeader("Вид строительства");
let select_constr_type = new SelectedContainer($('#selected-construction-type'), {
obj: sb_constr_main,
noTree: true,
onlyOne: true
});
sb_constr_main.connectSelectedContainer(select_constr_type);
}
function createLocations(url) {
sb_loc_main = new TreeSelect($('#sb-location-1'), {url, visible: true});
sb_loc_main.setHeader("Местоположение");
let select_loc = new SelectedContainer($('#selected-location'),
{
obj: sb_loc_main,
onlyOne: true
});
sb_loc_main.connectSelectedContainer(select_loc);
let sb_loc_1 = new TreeSelect($('#sb-location-2'), {obj: sb_loc_main});
let sb_loc_2 = new TreeSelect($('#sb-location-3'), {obj: sb_loc_main});
sb_loc_main.setNearbySelectBox(sb_loc_1);
sb_loc_1.setNearbySelectBox(sb_loc_2, sb_loc_main);
sb_loc_2.setNearbySelectBox("", sb_loc_1);
}
function createRealty(url) {
let sb_realty = new SelectOrCreate($('#sb-realty'), {url, visible: true});
sb_realty.setHeader(" ");
let select_realty = new SelectedContainerCreate($('#selected-realty'),
{
obj: sb_realty,
noTree: true,
onlyOne: true,
noHeader: true
});
sb_realty.connectSelectedContainer(select_realty);
sb_realty.setLinkBoxes([sb_loc_main, sb_constr_main, sb_build_main]);
select_realty.on("add", () => {
$('#checkbox-sb-realty').attr("disabled", true)
});
select_realty.on("remove", () => {
$('#checkbox-sb-realty').attr("disabled", false)
});
sb_realty.dataPromise.then(function () {
let $realty = $('#sb-realty');
let check = $('#checkbox-sb-realty');
if (!check.prop("checked")) {
$realty.hide();
}
}
);
let sb_realty_top = new NoTreeSelect($('#sb-realty-top'), {url, visible: true});
sb_realty_top.setHeader("Объект");
sb_realty_top.connectSelectedContainer(select_realty);
sb_realty_top.dataPromise.then(function () {
if (!sb_realty_top.dataTree.data.length) {
sb_realty_top.hide()
}else{
let id = window.location.hash.replace("#", "");
if (id) sb_realty_top.setElementById(id);
}
});
select_realty.on("add", (args)=> {
//TODO: Костыли!!!
$('#checkbox-sb-realty').prop("checked", true);
sb_realty.show();
let id = args[0];
if (id.text) return;
let el = sb_realty.dataTree.getElementById(id);
sb_realty_top.dataPromise.then(function () {
sb_realty_top.$searchInput.val(el.name);
sb_realty_top.selectedEl.id = id;
sb_realty_top.selectedEl.value = el.name;
});
sb_realty.selectedEl.id = id;
sb_realty._fillBoxes();
})
}
let sb_loc_main, sb_constr_main, sb_build_main;
// Тип работы
createSpecs('/api/specializations_flat');
// Классификация здания
createBuildingClass('/api/building_classifications');
// createConstructionType('/api/construction_type');
// createLocations('/api/locations_flat');
// createRealty('/api/realties/current_user')
});

@ -0,0 +1,11 @@
import {imageUploadInit} from './seeds/image_upload'
import {scrollOnRequiredInit} from './seeds/scroll_on_required'
import {sendFormData} from './seeds/ajax_send_form_data'
$(function () {
imageUploadInit();
scrollOnRequiredInit();
window.sendFormData = sendFormData;
window.scrollOnRequiredInit = scrollOnRequiredInit;
});

@ -1,4 +1,5 @@
//TODO: оформить Upload'еры в виде классов
//require: <script src='{% static "my-libs.js" %}'></script>
import {humanFileSize} from '../utils'
// function previewImg() {

@ -69,7 +69,7 @@ textarea.description
font-style: italic
.btn-simple
text-transform: uppercase
//text-transform: uppercase
border-radius: 40px
padding: 10px 15px
border: 1px solid #FF0029
@ -207,10 +207,11 @@ textarea.description
border-top: 1px solid #CFCFCF
.btn-plus
line-height: 115%
white-space: normal
padding-left: 18%
color: #FF0029
line-height: 1em
//line-height: 1em
font-family: Arial-MT-Regular, sans-serif
font-size: 10pt

@ -272,6 +272,34 @@ class RealtyFormNew(forms.ModelForm):
# self.fields['location'].queryset = Location.objects # Migrate with this enabled
class PortfolioFormNew(forms.ModelForm):
photos = forms.ModelMultipleChoiceField(
queryset=PortfolioPhoto.objects.none(),
# widget=forms.CheckboxSelectMultiple,
required=False,
)
class Meta:
model = Portfolio
fields = (
'name',
'building_classification', # Классификация здания
'specializations', # Тип работы
'description',
'photos'
)
def __init__(self, *args, **kwargs):
try:
self.request = kwargs.pop('request')
except KeyError:
pass
super().__init__(*args, **kwargs)
self.fields['specializations'].required = True
if self.instance.pk:
self.fields['photos'].queryset = self.instance.photos
class PortfolioForm(forms.ModelForm):
building_classification = TreeNodeChoiceField(
BuildingClassfication.objects.exclude(name='_root'),

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-12-27 16:47
from __future__ import unicode_literals
from django.db import migrations
import mptt.fields
class Migration(migrations.Migration):
dependencies = [
('specializations', '0004_auto_20161014_2344'),
('projects', '0052_realty_created'),
]
operations = [
migrations.AddField(
model_name='portfolio',
name='specializations',
field=mptt.fields.TreeManyToManyField(blank=True, to='specializations.Specialization'),
),
]

@ -6,7 +6,7 @@ from django.db.models import Q
from django.utils import timezone
from hitcount.models import HitCountMixin
from mptt.managers import TreeManager
from mptt.models import TreeForeignKey, MPTTModel
from mptt.models import TreeForeignKey, MPTTModel, TreeManyToManyField
from django.db.models import Sum, Count
_.map = _.map_;
@ -356,20 +356,24 @@ class Candidate(models.Model):
class Portfolio(models.Model):
budget = models.DecimalField(max_digits=10, decimal_places=0, default=0, null=True, blank=True)
name = models.CharField(max_length=255)
building_classification = TreeForeignKey(BuildingClassfication, related_name='portfolios', null=True, blank=True)
construction_type = models.ForeignKey(ConstructionType, related_name='portfolios', null=True, blank=True)
# @deprecated
specialization = TreeForeignKey(Specialization, related_name='portfolios', null=True, blank=True)
specializations = TreeManyToManyField(Specialization, blank=True)
description = models.TextField(blank=True)
created = models.DateTimeField(auto_now_add=True, auto_created=True)
user = models.ForeignKey(User, related_name='portfolios', null=True, blank=True)
worksell = models.BooleanField(default=False)
# @deprecated all fields
budget = models.DecimalField(max_digits=10, decimal_places=0, default=0, null=True, blank=True)
construction_type = models.ForeignKey(ConstructionType, related_name='portfolios', null=True, blank=True)
currency = models.CharField(max_length=20, default='rur', choices=CURRENCIES, null=True, blank=True)
description = models.TextField(blank=True)
location = TreeForeignKey('common.Location', related_name='portfolios', null=True, blank=True)
name = models.CharField(max_length=255)
specialization = TreeForeignKey(Specialization, related_name='portfolios', null=True, blank=True)
term = models.IntegerField(default=0, null=True, blank=True)
term_type = models.CharField(max_length=20, choices=TERM_TYPES, default='hour', null=True, blank=True)
user = models.ForeignKey(User, related_name='portfolios', null=True, blank=True)
work_type = models.IntegerField(default=1, choices=Project.WORK_TYPES)
worksell = models.BooleanField(default=False)
def __str__(self):
return self.name

@ -63,7 +63,7 @@
<div class="header">&nbsp;</div>
<div href="#" onclick="return false" data-toggle="modal"
data-target="#projectWorkTypeSuggestionModal"
style="text-transform: none; line-height: 115%"
{# style="text-transform: none; line-height: 115%"#}
class="btn btn-simple btn-plus">Нет нужной специальности хочу добавить
</div>
</div>
@ -123,13 +123,6 @@
<ul style="float: none" class="list-new-new">
{% for file in form.files.field.queryset.all %}
{# <li class="existing-file-widget">#}
{# <input type="checkbox" name="{{ form.files.html_name }}"#}
{# value="{{ file.pk }}" checked#}
{# style='display: none'>#}
{# <p class="file-upload-label">{{ file.file.name|basename }} {{ file.file.size|filesizeformat }}</p>#}
{# <div class="existing-file-remove-btn"></div>#}
{# </li>#}
<li class="file-upload-widget">
<input type="hidden" name="files" class="file-upload-input" value="{{ file.pk }}">
<span class="file-upload-label">{{ file.file.name|basename }} {{ file.file.size|filesizeformat }}</span>

@ -0,0 +1,185 @@
{% extends 'partials/_base.html' %}
{% load i18n %}
{% load staticfiles %}
{% load sass_tags %}
{% load thumbnail %}
{% block personal_css %}
<link rel='stylesheet' href='{% sass_src "sass/components/custom-components.sass" %}'>
<link rel='stylesheet' href='{% sass_src "lib/proekton-components/sass/components.sass" %}'>
{% endblock %}
{% block content %}
{% include 'partials/modals/project_work_type_suggestion.html' %}
<div class="container main-scope">
<div class="row title-scope">
<div class="col-lg-12">
<h1>{% if pk %}Редактировать{% else %}Добавить{% endif %} работу в портфолио</h1>
</div>
</div>
<div class="row main-content">
<div class="col-lg-12">
<form action="
{% if pk %}
{% url 'projects:contractor-portfolio-edit' pk=pk %}
{% else %}
{% url 'projects:contractor-portfolio-create' %}
{% endif %}
"
method="POST"
enctype="multipart/form-data" novalidate>
{% csrf_token %}
<div class="row">
<div class="col-lg-9">
<div class="simple-field">
<div class="header">Название проекта</div>
<i class="fa fa-question-circle-o" aria-hidden="true"></i>
<span class="required {% if form.name.errors %}error{% endif %}">Обязательно</span>
<input value="{{ form.name.value }}" name="name"
class="simple-input italic"
placeholder="Пример: Дизайн квартиры, Армирование фундамента, Конструкции перекрытия и т.д."
autocomplete="off"
required>
</div>
</div>
<div class="col-lg-3">
<div id="sb-building-classification">
</div>
<div class="vertical-child" id="sb-building-sub-classification">
</div>
<div class="selected-container" id="selected-building-classification">
<input type="hidden"
name="building_classification"
value="{{ form.building_classification.value }}">
</div>
</div>
</div>
<div class="row vertical-child mod-align-bottom">
<div class="col-lg-9">
<div class="header">Тип работы</div>
<i class="fa fa-question-circle-o" aria-hidden="true"></i>
<span class="required {% if form.specializations.errors %}error{% endif %}">Обязательно</span>
{# Для поиска заголовка обязательного поля по name= FIXME: fixit#}
<div hidden name="specializations"></div>
<div class="" id="select-box-1"></div>
</div>
<div class="col-lg-3">
<div class="header">&nbsp;</div>
<div href="#" onclick="return false" data-toggle="modal"
data-target="#projectWorkTypeSuggestionModal"
{# style="text-transform: none; line-height: 115%"#}
class="btn btn-simple btn-plus">Нет нужной специальности хочу добавить
</div>
</div>
</div>
<div class="row">
<div class="col-lg-3">
<div class="vertical-child" id="select-box-2">
</div>
</div>
<div class="col-lg-3">
<div class="vertical-child" id="select-box-3">
</div>
</div>
<div class="col-lg-3">
<div class="vertical-child" id="select-box-4">
</div>
</div>
<div class="col-lg-3">
<div class="vertical-child" id="select-box-5">
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="selected-container horizontal" id="selected-spec">
<input type="hidden" name="specializations"
value="{{ form.specializations.value }}">
</div>
</div>
</div>
<div style="margin-top: 45px" class="row">
<div class="col-lg-12">
<div class="simple-field">
<div class="header">Подробно опишите проект</div>
<i class="fa fa-question-circle-o" aria-hidden="true" title=""></i>
<textarea name="description" class="description"
rows="6">{{ form.description.value }}</textarea>
</div>
<div class="documentsChat mod" id="fileUploadContainer">
<div style="display: inline-block;vertical-align: middle;">
<div style="display: inline-block;vertical-align: inherit;"
class="upload-new paper-clip">
<p id="fileUploadAddBtn" style="margin: 0">прикрепить файл</p>
</div>
<span style="display: inline-block;vertical-align: inherit;">ДО 100 ФАЙЛОВ</span>
</div>
<div class="row list-new-new" style="margin-top: 10px">
{% for image in form.photos.field.queryset.all %}
<div class="col-lg-3 file-upload-widget">
<input type="hidden" name="photos" class="file-upload-input" value="{{ image.pk }}">
<div class="preview-container">
<div class="btn close file-upload-remove-img-btn">&times;</div>
<div class="preview">
{% thumbnail image.img "250x200" crop="center" as im %}
<img src="{{ im.url }}">
{% endthumbnail %}
</div>
</div>
{# <span style="display:none" class="file-upload-label"></span>#}
</div>
{% endfor %}
<div class="col-lg-3 file-upload-widget" style="display: none">
<input type="file" name="new_files" class="file-upload-input"
style="position: absolute; top: -1000px; left: -1000px">
<div class="preview-container">
<div class="btn close file-upload-remove-img-btn">&times;</div>
<div class="preview"></div>
</div>
<span style="display:none" class="file-upload-label"></span>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="separator">
<div class="border"></div>
</div>
</div>
</div>
<div class="row top-line">
<div class="col-lg-3" style="text-align: center">
<input style="width: 100%" type="submit" class="btn btn-simple"
onclick="sendFormData(event)"
value="{% if pk %}СОХРАНИТЬ{% else %}РАЗМЕСТИТЬ{% endif %} ПОРТФОЛИО">
</div>
<div class="col-lg-3 col-lg-offset-6" style="text-align: center">
<input style="width: 100%" type="button" class="btn btn-simple"
value="ПРЕДВАРИТЕЛЬНЫЙ ПРОСМОТР">
</div>
</div>
</form>
</div> <!-- content -->
</div>
{# <ul id="popups-storage" hidden>#}
{# {% if form.errors %}#}
{# <li class="error">Форма заполнена неполностью</li>#}
{# {% endif %}#}
{# <li class="info">Message-1</li>#}
{# <li class="warning">Message-1</li>#}
{# </ul>#}
{# {% include 'partials/inc-message-popup.html' %}#}
</div>
{% endblock %}
{% block personal_js %}
<script src='{% static "my-libs.js" %}'></script>
<script src='{% static "js/build/init_portfolio_create_edit.js" %}'></script>
<script src='{% static "js/build/portfolio_create_edit.js" %}'></script>
{% endblock %}

@ -6,10 +6,11 @@ from .views import (
add_candidate,
ArbitrationCreateView,
CandidateDeleteView,
contractor_portfolio_create,
# contractor_portfolio_create,
ContractorAnswerArchiveView,
ContractorPortfolioCreateView,
ContractorPortfolioTrashView,
ContractorPortfolioUpdateView,
ContractorPortfolioEditView,
CustomerOfferOrderView,
CustomerProjectCreateView,
CustomerProjectDeleteView,
@ -49,9 +50,9 @@ urlpatterns = [
urls.url(r'^arbitration/create/$', ArbitrationCreateView.as_view(), name='arbitration-create'),
urls.url(r'^answer/move/archive/$', ContractorAnswerArchiveView.as_view(), name='contractor-answer-archive'),
urls.url(r'^portfolio/create/$', contractor_portfolio_create, name='contractor-portfolio-create'),
urls.url(r'^portfolio/create/$', ContractorPortfolioCreateView.as_view(), name='contractor-portfolio-create'),
urls.url(r'^portfolio/(?P<pk>\d+)/$', PortfolioDetail.as_view(), name='contractor-portfolio-detail'),
urls.url(r'^portfolio/(?P<pk>\d+)/edit/$', ContractorPortfolioUpdateView.as_view(),
urls.url(r'^portfolio/(?P<pk>\d+)/edit/$', ContractorPortfolioEditView.as_view(),
name='contractor-portfolio-edit'),
urls.url(r'^portfolio/(?P<pk>\d+)/trash/$', ContractorPortfolioTrashView.as_view(),
name='contractor-portfolio-trash'),

@ -16,6 +16,7 @@ from django.shortcuts import render, get_object_or_404, redirect
from django.views.generic import DetailView, CreateView, DeleteView, View, UpdateView, TemplateView
from hitcount.models import HitCount
from hitcount.views import HitCountMixin
from hitcount.views import HitCountMixin
import re
@ -51,6 +52,7 @@ from .forms import (
CustomerProjectTrashForm,
PortfolioEditForm,
PortfolioForm,
PortfolioFormNew,
ProjectAnswerForm,
ProjectAnswerMessageForm,
ProjectWorkTypeSuggestionForm,
@ -559,8 +561,8 @@ class CustomerProjectEditView(BaseMixin, View):
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?
# print("files = ", form.cleaned_data.get('files'))
project.files = form.cleaned_data.get('files')
project.save()
form.save_m2m()
@ -867,85 +869,115 @@ class CustomerOfferOrderView(View):
return HttpResponseRedirect(redirect_url)
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()))
if form.is_valid():
duplicate = form.cleaned_data.get('duplicate')
instance = form.save(commit=False)
instance.user = request.user
instance.save()
if duplicate:
work_sell = WorkSell()
work_sell.name = instance.name
work_sell.budget = instance.budget
work_sell.building_classification = instance.building_classification
work_sell.construction_type = instance.construction_type
work_sell.currency = instance.currency
work_sell.description = instance.description
work_sell.term = instance.term
work_sell.term_type = instance.term_type
work_sell.contractor = instance.user
work_sell.save()
images_ids = request.POST.get('images_ids').split(';')[:-1]
for pk in images_ids:
picture = Picture.objects.get(pk=pk)
temp_file = ContentFile(picture.file.read())
temp_file.name = picture.file.name
p_photo = PortfolioPhoto()
p_photo.img = temp_file
p_photo.portfolio = instance
p_photo.save()
if duplicate:
w_photo = WorkSellPhoto()
w_photo.img = temp_file
w_photo.worksell = work_sell
w_photo.save()
data = {'status': 'ok'}
else:
data = {'status': 'no', 'form_errors': form.errors}
return HttpResponse(json.dumps(data), content_type='application/json')
class ContractorPortfolioCreateView(BaseMixin, View):
form_class = PortfolioFormNew
work_type_suggestion_form = ProjectWorkTypeSuggestionForm
template_name = 'portfolio_create_edit.html'
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_contractor():
return super().dispatch(request, *args, **kwargs)
else:
raise Http404
raise PermissionDenied
def get(self, request, *args, **kwargs):
context = self.get_context_data(**_.merge({}, request.GET, kwargs))
class ContractorPortfolioUpdateView(BaseMixin, UpdateView):
model = Portfolio
form_class = PortfolioEditForm
template_name = 'contractor_portfolio_edit.html'
form = self.form_class()
work_type_suggestion_form = self.work_type_suggestion_form(request=request, prefix='work_type_suggestion')
def get_success_url(self):
return reverse('projects:contractor-portfolio-detail', kwargs={'pk': self.object.pk})
context.update({
'form': form,
'work_type_suggestion_form': work_type_suggestion_form,
})
def form_valid(self, form):
portfolio = form.instance
return render(request, self.template_name, context)
photos = form.cleaned_data['photos']
def post(self, request, *args, **kwargs):
if request.POST.get('specializations') == "":
request.POST.pop('specializations')
else:
request.POST.setlist('specializations', request.POST.get('specializations', "").split(','))
form = self.form_class(request.POST, request=request)
# print('POST = ', request.POST)
if form.is_valid():
portfolio = form.save(commit=False)
portfolio.user = request.user
portfolio.save()
form.save_m2m()
for file in request.FILES.getlist('new_files'):
PortfolioPhoto.objects.create(img=file, portfolio=portfolio)
messages.info(request, 'Портфолио успешно создано')
redirect_to = reverse('projects:contractor-portfolio-detail', kwargs={'pk': portfolio.pk})
if request.is_ajax():
return JsonResponse({'redirect_to': redirect_to}, status=200)
return redirect(redirect_to)
else:
context = self.get_context_data(**kwargs)
context.update({'form': form})
if request.is_ajax():
data = json.dumps(form.errors)
print("form errors = ", data)
return HttpResponse(content=data, status=400, content_type='application/json')
return render(request, self.template_name, context)
# # Doesn't work:
#
# portfolio.photos = photos
# portfolio.save()
PortfolioPhoto.objects.filter(portfolio=portfolio).delete()
class ContractorPortfolioEditView(BaseMixin, View):
form_class = PortfolioFormNew
work_type_suggestion_form = ProjectWorkTypeSuggestionForm
template_name = 'portfolio_create_edit.html'
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_contractor():
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
def get(self, request, *args, **kwargs):
print("request.GET = ", request.GET)
portfolio = get_object_or_404(Portfolio, pk=kwargs.get('pk'))
form = self.form_class(instance=portfolio, request=request)
work_type_suggestion_form = self.work_type_suggestion_form(request=request, prefix='work_type_suggestion')
context = self.get_context_data(**_.merge({}, request.GET, kwargs))
for photo in photos:
PortfolioPhoto.objects.create(img=photo.img, portfolio=portfolio)
context.update({
'form': form,
'work_type_suggestion_form': work_type_suggestion_form,
})
live_images = form.cleaned_data['live_images']
return render(request, self.template_name, context)
for live_image in live_images:
new_image = ContentFile(live_image.file.read())
new_image.name = live_image.file.name
def post(self, request, *args, **kwargs):
print("request.POST = ", request.POST)
portfolio = get_object_or_404(Portfolio, pk=kwargs.get('pk'))
form = self.form_class(request.POST, request.FILES, request=request, instance=portfolio)
PortfolioPhoto.objects.create(img=new_image, portfolio=portfolio)
if form.is_valid():
portfolio = form.save(commit=False)
portfolio.user = request.user
print("clean photos = ", form.cleaned_data.get('photos'))
portfolio.photos = form.cleaned_data.get('photos')
portfolio.save()
form.save_m2m()
live_image.file.delete()
live_image.delete()
for file in request.FILES.getlist('new_files'):
PortfolioPhoto.objects.create(img=file, portfolio=portfolio)
return super().form_valid(form)
messages.info(request, 'Портфолио успешно создано')
redirect_to = reverse('projects:contractor-portfolio-detail', kwargs={'pk': portfolio.pk})
if request.is_ajax():
return JsonResponse({'redirect_to': redirect_to}, status=200)
return redirect(redirect_to)
else:
context = self.get_context_data(**kwargs)
context.update({'form': form})
if request.is_ajax():
data = json.dumps(form.errors)
print("form errors = ", data)
return HttpResponse(content=data, status=400, content_type='application/json')
return render(request, self.template_name, context)
class PortfolioDelete(DeleteView):

@ -182,7 +182,8 @@
{% if contractor.pk == request.user.pk %}
<div class="col-lg-9">
<p class="addWork">
<a href="#" data-toggle="modal" data-target="#portfolio-modal">Добавить работу</a>
{# <a href="#" data-toggle="modal" data-target="#portfolio-modal">Добавить работу</a>#}
<a href="{% url 'projects:contractor-portfolio-create' %}" >Добавить работу</a>
</p>
</div>

@ -23,8 +23,9 @@ module.exports = {
init_contractor_filter: "./assets/js/src/init_contractor_filter.js",
contractor_filter: "./assets/js/src/contractor_filter.js",
project_filter: "./assets/js/src/project_filter.js",
//portfolio
init_portfolio_create_edit: "./assets/js/src/init_portfolio_create_edit.js",
portfolio_create_edit: "./assets/js/src/portfolio_create_edit.js",
},
output: {

@ -236,11 +236,11 @@
</div>
{% endblock %}
{% block old_js %}
<script src='{% static "lib/jquery.fileupload/js/vendor/jquery.ui.widget.js" %}'></script>
<script src='{% static "lib/jquery.fileupload/js/jquery.iframe-transport.js" %}'></script>
<script src='{% static "lib/jquery.fileupload/js/jquery.fileupload.js" %}'></script>
<script src='{% static "lib/jquery.fileupload/js/jquery.fileupload-process.js" %}'></script>
<script src='{% static "lib/lodash/lodash.js" %}'></script>
{# <script src='{% static "lib/jquery.fileupload/js/vendor/jquery.ui.widget.js" %}'></script>#}
{# <script src='{% static "lib/jquery.fileupload/js/jquery.iframe-transport.js" %}'></script>#}
{# <script src='{% static "lib/jquery.fileupload/js/jquery.fileupload.js" %}'></script>#}
{# <script src='{% static "lib/jquery.fileupload/js/jquery.fileupload-process.js" %}'></script>#}
{# <script src='{% static "lib/lodash/lodash.js" %}'></script>#}
<script src='{% static "my-libs.js" %}'></script>
{% endblock %}
{% block js_block %}

@ -176,11 +176,11 @@ class WorkSellCreateView(BaseMixin, View):
work_type_suggestion_form = ProjectWorkTypeSuggestionForm
template_name = 'worksell_create.html'
# def dispatch(self, request, *args, **kwargs):
# if request.user.is_authenticated() and request.user.is_contractor():
# return super().dispatch(request, *args, **kwargs)
# else:
# raise PermissionDenied
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_contractor():
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
def get_context_data(self, **kwargs):
ctx = super().get_context_data()

Loading…
Cancel
Save