PR-62 Модификация формы заполнения портфолио

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

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

@ -179,7 +179,6 @@
} }
}); });
select_realty.on("add", function (args) { select_realty.on("add", function (args) {
console.log("realty add");
//TODO: Костыли!!! //TODO: Костыли!!!
$('#checkbox-sb-realty').prop("checked", true); $('#checkbox-sb-realty').prop("checked", true);
sb_realty.show(); sb_realty.show();

File diff suppressed because it is too large Load Diff

@ -0,0 +1,273 @@
/******/ (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__(11);
var _scroll_on_required = __webpack_require__(8);
var _ajax_send_form_data = __webpack_require__(12);
$(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 */
/***/ 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;
/***/ },
/* 3 */,
/* 4 */,
/* 5 */,
/* 6 */,
/* 7 */,
/* 8 */
/***/ 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;
/***/ },
/* 9 */,
/* 10 */,
/* 11 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.imageUploadInit = undefined;
var _utils = __webpack_require__(2);
// 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}
/***/ },
/* 12 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.sendFormData = undefined;
var _utils = __webpack_require__(2);
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'еры в виде классов //TODO: оформить Upload'еры в виде классов
//require: <script src='{% static "my-libs.js" %}'></script>
import {humanFileSize} from '../utils' import {humanFileSize} from '../utils'
// function previewImg() { // function previewImg() {

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

@ -272,6 +272,34 @@ class RealtyFormNew(forms.ModelForm):
# self.fields['location'].queryset = Location.objects # Migrate with this enabled # 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): class PortfolioForm(forms.ModelForm):
building_classification = TreeNodeChoiceField( building_classification = TreeNodeChoiceField(
BuildingClassfication.objects.exclude(name='_root'), 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 django.utils import timezone
from hitcount.models import HitCountMixin from hitcount.models import HitCountMixin
from mptt.managers import TreeManager 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 from django.db.models import Sum, Count
_.map = _.map_; _.map = _.map_;
@ -356,20 +356,24 @@ class Candidate(models.Model):
class Portfolio(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) 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) 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) 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) 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 = models.IntegerField(default=0, null=True, blank=True)
term_type = models.CharField(max_length=20, choices=TERM_TYPES, default='hour', 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) work_type = models.IntegerField(default=1, choices=Project.WORK_TYPES)
worksell = models.BooleanField(default=False)
def __str__(self): def __str__(self):
return self.name return self.name

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

@ -0,0 +1,181 @@
{% extends 'partials/_base.html' %}
{% load i18n %}
{% load staticfiles %}
{% load sass_tags %}
{% 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>
{% for image in form.photos.field.queryset.all %}
<input type="hidden" name="files" class="file-upload-input"
<div class="col-lg-3 file-upload-widget" style="display: none">
<div class="preview-container">
<div class="btn close file-upload-remove-img-btn">&times;</div>
<div class="preview">
<img src="{{ image.img.url }}">
</div>
</div>
<span style="display:none" class="file-upload-label"></span>
</div>
{% endfor %}
<div class="row list-new-new" style="margin-top: 10px">
<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, add_candidate,
ArbitrationCreateView, ArbitrationCreateView,
CandidateDeleteView, CandidateDeleteView,
contractor_portfolio_create, # contractor_portfolio_create,
ContractorAnswerArchiveView, ContractorAnswerArchiveView,
ContractorPortfolioCreateView,
ContractorPortfolioTrashView, ContractorPortfolioTrashView,
ContractorPortfolioUpdateView, ContractorPortfolioEditView,
CustomerOfferOrderView, CustomerOfferOrderView,
CustomerProjectCreateView, CustomerProjectCreateView,
CustomerProjectDeleteView, CustomerProjectDeleteView,
@ -49,9 +50,9 @@ urlpatterns = [
urls.url(r'^arbitration/create/$', ArbitrationCreateView.as_view(), name='arbitration-create'), 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'^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+)/$', 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'), name='contractor-portfolio-edit'),
urls.url(r'^portfolio/(?P<pk>\d+)/trash/$', ContractorPortfolioTrashView.as_view(), urls.url(r'^portfolio/(?P<pk>\d+)/trash/$', ContractorPortfolioTrashView.as_view(),
name='contractor-portfolio-trash'), 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 django.views.generic import DetailView, CreateView, DeleteView, View, UpdateView, TemplateView
from hitcount.models import HitCount from hitcount.models import HitCount
from hitcount.views import HitCountMixin from hitcount.views import HitCountMixin
from hitcount.views import HitCountMixin
import re import re
@ -51,6 +52,7 @@ from .forms import (
CustomerProjectTrashForm, CustomerProjectTrashForm,
PortfolioEditForm, PortfolioEditForm,
PortfolioForm, PortfolioForm,
PortfolioFormNew,
ProjectAnswerForm, ProjectAnswerForm,
ProjectAnswerMessageForm, ProjectAnswerMessageForm,
ProjectWorkTypeSuggestionForm, ProjectWorkTypeSuggestionForm,
@ -877,85 +879,135 @@ class CustomerOfferOrderView(View):
return HttpResponseRedirect(redirect_url) return HttpResponseRedirect(redirect_url)
def contractor_portfolio_create(request): # TODO: pekopt: shit. rewrite using generic class ContractorPortfolioCreateView(BaseMixin, View):
if request.is_ajax(): form_class = PortfolioFormNew
form = PortfolioForm(data=request.POST) work_type_suggestion_form = ProjectWorkTypeSuggestionForm
# import code; code.interact(local=dict(globals(), **locals())) 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):
context = self.get_context_data(**_.merge({}, request.GET, kwargs))
form = self.form_class()
work_type_suggestion_form = self.work_type_suggestion_form(request=request, prefix='work_type_suggestion')
context.update({
'form': form,
'work_type_suggestion_form': work_type_suggestion_form,
})
return render(request, self.template_name, context)
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(): if form.is_valid():
duplicate = form.cleaned_data.get('duplicate') portfolio = form.save(commit=False)
instance = form.save(commit=False) portfolio.user = request.user
instance.user = request.user portfolio.save()
instance.save() form.save_m2m()
if duplicate: for file in request.FILES.getlist('new_files'):
work_sell = WorkSell() PortfolioPhoto.objects.create(img=file, portfolio=portfolio)
work_sell.name = instance.name messages.info(request, 'Портфолио успешно создано')
work_sell.budget = instance.budget redirect_to = reverse('projects:contractor-portfolio-detail', kwargs={'pk': portfolio.pk})
work_sell.building_classification = instance.building_classification if request.is_ajax():
work_sell.construction_type = instance.construction_type return JsonResponse({'redirect_to': redirect_to}, status=200)
work_sell.currency = instance.currency return redirect(redirect_to)
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: else:
data = {'status': 'no', 'form_errors': form.errors} context = self.get_context_data(**kwargs)
return HttpResponse(json.dumps(data), content_type='application/json') context.update({'form': form})
else: if request.is_ajax():
raise Http404 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 ContractorPortfolioUpdateView(BaseMixin, UpdateView): class ContractorPortfolioEditView(BaseMixin, View):
model = Portfolio form_class = PortfolioFormNew
form_class = PortfolioEditForm work_type_suggestion_form = ProjectWorkTypeSuggestionForm
template_name = 'contractor_portfolio_edit.html' template_name = 'portfolio_create_edit.html'
def get_success_url(self): def get(self, request, *args, **kwargs):
return reverse('projects:contractor-portfolio-detail', kwargs={'pk': self.object.pk}) 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')
def form_valid(self, form): context = self.get_context_data(**_.merge({}, request.GET, kwargs))
portfolio = form.instance
context.update({
'form': form,
'work_type_suggestion_form': work_type_suggestion_form,
})
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
# print("request.POST = ", request.POST)
project = get_object_or_404(request.user.customer_projects, pk=kwargs.get('pk'))
form = self.form_class(request.POST, request.FILES, request=request, instance=project)
photos = form.cleaned_data['photos'] form.is_valid()
realty = form.cleaned_data.get('realty')
# # Doesn't work: if realty:
# realty_form = self.realty_form(request.POST, instance=realty, request=request, prefix='realty_form')
# portfolio.photos = photos else:
# portfolio.save() realty_form = self.realty_form(request.POST, request=request, prefix='realty_form')
PortfolioPhoto.objects.filter(portfolio=portfolio).delete() 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 photo in photos: for file in request.FILES.getlist('new_files'):
PortfolioPhoto.objects.create(img=photo.img, portfolio=portfolio) ProjectFile.objects.create(file=file, project=project)
live_images = form.cleaned_data['live_images'] if not realty:
realty = realty_form.save(commit=False)
realty.user = request.user
if not request.POST.get('new_realty_name'):
realty.is_virtual = True
realty.name = request.POST.get('new_realty_name')
realty.save()
realty_form.save_m2m()
for live_image in live_images: project.realty = realty # Connect a realty with a project
new_image = ContentFile(live_image.file.read()) project.save()
new_image.name = live_image.file.name
PortfolioPhoto.objects.create(img=new_image, portfolio=portfolio) messages.info(request, 'Проект успешно отредактирован')
# redirect_to = request.POST.get('back')
redirect_to = reverse('projects:detail', kwargs={'pk': project.pk})
return redirect(redirect_to)
else:
if form.errors:
messages.info(request, (
'<p>Произошла ошибка (form)</p>'
'<pre>{form}</pre>'
).format(form=pformat(form.errors)))
live_image.file.delete() if realty_form and realty_form.errors:
live_image.delete() messages.info(request, (
'<p>Произошла ошибка (realty_form)</p>'
'<pre>{realty_form}</pre>'
).format(realty_form=pformat(realty_form.errors)))
return super().form_valid(form) context = self.get_context_data(**kwargs)
context.update({'form': form, 'realty_form': realty_form})
return render(request, self.template_name, context)
class PortfolioDelete(DeleteView): class PortfolioDelete(DeleteView):

@ -182,7 +182,8 @@
{% if contractor.pk == request.user.pk %} {% if contractor.pk == request.user.pk %}
<div class="col-lg-9"> <div class="col-lg-9">
<p class="addWork"> <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> </p>
</div> </div>

@ -18,7 +18,10 @@ module.exports = {
home_page: "./assets/js/src/home_page.js", home_page: "./assets/js/src/home_page.js",
//profile //profile
customer_profile: "./assets/js/src/customer_profile.js", customer_profile: "./assets/js/src/customer_profile.js",
user_profile_edit: "./assets/js/src/user_profile_edit.js" user_profile_edit: "./assets/js/src/user_profile_edit.js",
//portfolio
init_portfolio_create_edit: "./assets/js/src/init_portfolio_create_edit.js",
portfolio_create_edit: "./assets/js/src/portfolio_create_edit.js",
}, },

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

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

Loading…
Cancel
Save