diff --git a/assets/index.js b/assets/index.js index ead0214..42759a6 100644 --- a/assets/index.js +++ b/assets/index.js @@ -2,6 +2,8 @@ var API_PAGE_SIZE = 100 +var LIVE_IMAGE_UPLOAD_URL = '/common/live-image-upload/create/' +var STUB_IMAGE_URL = '/static/img/profile.jpg' @@ -514,39 +516,98 @@ function loadAllPhotos(portfIds) { // Live image upload --------------------------------------- -var $liveImageUploadContainer = $('.-live-image-upload-container').first() - -var $avatarImage = $liveImageUploadContainer.find('.-avatar-image').first() -var $liveImageUpload = $liveImageUploadContainer.find('.-live-image-upload').first() -var $liveImageId = $liveImageUploadContainer.find('.-live-image-id').first() -var $liveImageDelete = $liveImageUploadContainer.find('.-live-image-delete').first() -var stubImageUrl = $liveImageUploadContainer.data('stubImageUrl') - -$liveImageUpload.fileupload({ - dataType: 'json', +;(function() { + var $liveImageUploadContainer = $('.-live-image-upload-container').first() + + var $avatarImage = $liveImageUploadContainer.find('.-avatar-image').first() + var $liveImageUpload = $liveImageUploadContainer.find('.-live-image-upload').first() + var $liveImageId = $liveImageUploadContainer.find('.-live-image-id').first() + var $liveImageDelete = $liveImageUploadContainer.find('.-live-image-delete').first() + + $avatarImage.attr('orig-src', $avatarImage.attr('src')) - done: function($evt, data) { - var image = data.result.files[0] + $liveImageUpload.fileupload({ + url: LIVE_IMAGE_UPLOAD_URL, + dataType: 'json', - $avatarImage.attr('src', image.thumbnailUrl) - $liveImageId.val(image.id) + done: function($evt, data) { + var image = data.result + + $avatarImage.attr('src', image.thumbnailUrl) + $liveImageId.val(image.id) + + $liveImageDelete.data('url', image.deleteUrl) + $liveImageDelete.css('display', 'block') + } + }) + + $liveImageDelete.on('click', function($evt) { + var $that = $(this) - $liveImageDelete.data('url', image.deleteUrl) - $liveImageDelete.css('display', 'block') - } -}) + $.post($that.data('url')).then(function(res) { + if (res.status == 'success') { + $avatarImage.attr('src', $avatarImage.attr('orig-src') || STUB_IMAGE_URL) + $liveImageId.val('') + $that.css('display', 'none') + } + }) + }) +}()) -$liveImageDelete.on('click', function($evt) { - var $that = $(this) + + + + + + + + +// Live image upload (multiple) ----------------------------------- + + +;(function() { + var $container = $('.-live-image-upload-multiple-container').first() + + var $liveImageUpload = $container.find('.-live-image-upload').first() + var liveImageDeleteClass = '.-live-image-delete' + + var templ = _.template($container.find('.-templ').first().html()) + var $res = $container.find('.-res').first() + + var images = [] - $.post($that.data('url')).then(function(res) { - if (res.status == 'success') { - $avatarImage.attr('src', stubImageUrl) - $liveImageId.val('') - $that.css('display', 'none') + render() + + $liveImageUpload.fileupload({ + url: LIVE_IMAGE_UPLOAD_URL, + dataType: 'json', + + done: function($evt, data) { + var image = data.result + images.push(image) + render() } }) -}) + + $container.on('click', liveImageDeleteClass, function($evt) { + var $that = $(this) + var image = _.find({id: $that.data('imageId')}, images) + + $.post(image.deleteUrl).then(function(res) { + if (res.status == 'success') { + remove(images, image) + render() + } + }) + }) + + + // Utils -------------------------------- + + function render() { + $res.html(templ({images: images})) + } +}()) @@ -754,6 +815,10 @@ function getLocationTree(locId) { // Utils ----------------------------------------------- +var _call = Function.prototype.call +var _splice = _call.bind(Array.prototype.splice) + + function humanFileSize(bytes, si) { var thresh = si ? 1000 : 1024 @@ -794,6 +859,12 @@ function format() { } +function remove(coll, item) { + _splice(coll, _.indexOf(item, coll), 1) + return coll +} + + function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie != '') { @@ -808,6 +879,3 @@ function getCookie(name) { } return cookieValue; } - - - diff --git a/assets/lib/jquery-jgrowl/jquery.jgrowl.min.css b/assets/lib/jquery-jgrowl/jquery.jgrowl.min.css new file mode 100755 index 0000000..ea39484 --- /dev/null +++ b/assets/lib/jquery-jgrowl/jquery.jgrowl.min.css @@ -0,0 +1 @@ +.jGrowl{z-index:9999;color:#fff;font-size:12px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;position:fixed}.jGrowl.top-left{left:0;top:0}.jGrowl.top-right{right:0;top:0}.jGrowl.bottom-left{left:0;bottom:0}.jGrowl.bottom-right{right:0;bottom:0}.jGrowl.center{top:0;width:50%;left:25%}.jGrowl.center .jGrowl-closer,.jGrowl.center .jGrowl-notification{margin-left:auto;margin-right:auto}.jGrowl-notification{background-color:#000;opacity:.9;-ms-filter:alpha(90);filter:alpha(90);zoom:1;width:250px;padding:10px;margin:10px;text-align:left;display:none;border-radius:5px;min-height:40px}.jGrowl-notification .ui-state-highlight,.jGrowl-notification .ui-widget-content .ui-state-highlight,.jGrowl-notification .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}.jGrowl-notification .jGrowl-header{font-weight:700;font-size:.85em}.jGrowl-notification .jGrowl-close{background-color:transparent;color:inherit;border:none;z-index:99;float:right;font-weight:700;font-size:1em;cursor:pointer}.jGrowl-closer{background-color:#000;opacity:.9;-ms-filter:alpha(90);filter:alpha(90);zoom:1;width:250px;padding:10px;margin:10px;display:none;border-radius:5px;padding-top:4px;padding-bottom:4px;cursor:pointer;font-size:.9em;font-weight:700;text-align:center}.jGrowl-closer .ui-state-highlight,.jGrowl-closer .ui-widget-content .ui-state-highlight,.jGrowl-closer .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}@media print{.jGrowl{display:none}} \ No newline at end of file diff --git a/common/views.py b/common/views.py index a09281c..9768201 100644 --- a/common/views.py +++ b/common/views.py @@ -92,15 +92,16 @@ class LiveImageUploadCreateView(NoCsrfMixin, LoginRequiredMixin, View): live_img = LiveImageUpload.objects.create(file=image) - return JsonResponse({'files': [{ + return JsonResponse({ 'id': live_img.pk, 'name': live_img.file.name, 'size': live_img.file.size, 'url': live_img.file.url, 'thumbnailUrl': get_thumbnail(live_img.file, '235x224', crop='center').url, + 'smallThumbnailUrl': get_thumbnail(live_img.file, '200x200', crop='center').url, 'deleteUrl': reverse('common:live-image-upload-delete', kwargs={'pk': live_img.pk}), 'deleteType': 'POST', - }]}) + }) class LiveImageUploadDeleteView(NoCsrfMixin, LoginRequiredMixin, View): diff --git a/projects/forms.py b/projects/forms.py index c2a8977..9b3aa2f 100644 --- a/projects/forms.py +++ b/projects/forms.py @@ -82,6 +82,8 @@ class ProjectFilterRealtyForm(forms.ModelForm): class CustomerProjectEditForm(forms.ModelForm): + # Define a form field manually for a reverse model vield: + files = forms.ModelMultipleChoiceField( queryset=ProjectFile.objects.none(), widget=forms.CheckboxSelectMultiple, @@ -184,6 +186,43 @@ class PortfolioForm(forms.ModelForm): } +class PortfolioEditForm(forms.ModelForm): + # Define a form field manually for a reverse model vield: + + photos = forms.ModelMultipleChoiceField( + queryset=PortfolioPhoto.objects.none(), + widget=forms.CheckboxSelectMultiple, + required=False, + ) + + class Meta: + model = Portfolio + + fields = ( + 'building_classification', + 'construction_type', + 'currency', + 'name', + 'photos', + 'term_type', + ) + + # fields = '__all__' + # + # widgets = { + # 'construction_type': forms.Select(attrs={'class': 'selectpicker'}), + # 'building_classification': forms.Select(attrs={'class': 'selectpicker'}), + # 'currency': forms.Select(attrs={'class': 'selectpicker'}), + # 'term_type': forms.Select(attrs={'class': 'selectpicker'}), + # } + + def __init__(self, *args, **kwargs): + # self.request = kwargs.pop('request') + super().__init__(*args, **kwargs) + + self.fields['photos'].queryset = self.instance.photos.all() + + class ProjectAnswerForm(forms.ModelForm): text = forms.CharField(widget=forms.Textarea) diff --git a/projects/templates/contractor_portfolio_edit.html b/projects/templates/contractor_portfolio_edit.html index d48f470..998d6c0 100644 --- a/projects/templates/contractor_portfolio_edit.html +++ b/projects/templates/contractor_portfolio_edit.html @@ -1,6 +1,21 @@ {% extends 'partials/base.html' %} -{% load common_tags %} +{% load thumbnail %} + + +{% block head_css %} + +{% endblock %} {% block content %} {% include 'partials/header.html' %} @@ -85,19 +100,52 @@ -
Фотографии
- {% for p in form.instance.photos.all %} + + {% for photo in form.photos.field.queryset.all %}+ добавить файл (до 100 файлов)
diff --git a/projects/views.py b/projects/views.py index 2fcae3c..37cd920 100644 --- a/projects/views.py +++ b/projects/views.py @@ -45,6 +45,7 @@ from .forms import ( CustomerProjectEditForm, CustomerProjectRestoreForm, CustomerProjectTrashForm, + PortfolioEditForm, PortfolioForm, ProjectAnswerForm, ProjectAnswerMessageForm, @@ -755,9 +756,9 @@ def contractor_portfolio_create(request): # TODO: pekopt: shit. rewrite using g raise Http404 -class ContractorPortfolioUpdateView(UpdateView): +class ContractorPortfolioUpdateView(BaseMixin, UpdateView): model = Portfolio - form_class = PortfolioForm + form_class = PortfolioEditForm template_name = 'contractor_portfolio_edit.html' def get_success_url(self): diff --git a/templates/partials/base.html b/templates/partials/base.html index b270854..0a8b715 100644 --- a/templates/partials/base.html +++ b/templates/partials/base.html @@ -24,7 +24,7 @@ -{# #} + {% block head_css %}{% endblock %} @@ -70,7 +70,6 @@ - diff --git a/users/templates/contractor_office.html b/users/templates/contractor_office.html index d6111e4..adc7804 100644 --- a/users/templates/contractor_office.html +++ b/users/templates/contractor_office.html @@ -168,7 +168,7 @@