From c8478fd9355677d21b684c9c27fa217de8aaca18 Mon Sep 17 00:00:00 2001 From: Adam Sz <52573736+adam-sas-on@users.noreply.github.com> Date: Mon, 16 Aug 2021 17:03:45 +0200 Subject: [PATCH 01/15] Change button into file-type; 160 --- onboarding/src/Components/FormsEdit/FormDescription.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/onboarding/src/Components/FormsEdit/FormDescription.js b/onboarding/src/Components/FormsEdit/FormDescription.js index c982a4fb..ebde81d8 100644 --- a/onboarding/src/Components/FormsEdit/FormDescription.js +++ b/onboarding/src/Components/FormsEdit/FormDescription.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { useLocation } from "react-router-dom"; import { savePageDetails } from "../hooks/FormsEdit"; import ModalWarning from "../ModalWarning"; @@ -6,11 +6,14 @@ import { isValidUrl } from "../utils"; import bookOpenedIcon from "../../static/icons/book-opened.svg"; const FormDescription = ({ formId, formData }) => { + const formFilesRef = useRef([]); const location = useLocation(); const [formName, setFormName] = useState(""); const [link, setLink] = useState(""); const [description, setDescription] = useState(""); const [saveModal, setSaveModal ] = useState(<>>); + const [formFiles, updateFormFiles] = useState([]); + useEffect(() => { if(location.state && !formName) { @@ -97,9 +100,8 @@ const FormDescription = ({ formId, formData }) => { maxLength="200" /> {/* TODO: funkcjonalność przycisku */} - - Dołącz plik - + Dołącz plik + {e.preventDefault()}} />{/* Dołącz plik */} Tekst (liczba znaków: 1500) Date: Tue, 24 Aug 2021 14:46:54 +0200 Subject: [PATCH 02/15] Add model, view and endpoint to handle files for corresponding Page Page can have many files (many-to-one) When file is uploaded, white-list is taken into account but it checks extension only (no content type yet) Model stores: - a file - original name of file - size of file (to be in use when sum of sizes will be limited for each companies) - description for further information for user - corresponding page and company --- .../0008_files_for_page_of_package.py | 27 ++++++++++ onboarding/models.py | 51 +++++++++++++++++++ onboarding/serializers.py | 36 ++++++++++++- onboarding/urls.py | 3 +- onboarding/views.py | 44 +++++++++++++++- 5 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 onboarding/migrations/0008_files_for_page_of_package.py diff --git a/onboarding/migrations/0008_files_for_page_of_package.py b/onboarding/migrations/0008_files_for_page_of_package.py new file mode 100644 index 00000000..f278a768 --- /dev/null +++ b/onboarding/migrations/0008_files_for_page_of_package.py @@ -0,0 +1,27 @@ +# Generated by Django 3.1.10 on 2021-08-24 11:37 + +from django.db import migrations, models +import django.db.models.deletion +import onboarding.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('onboarding', '0007_model_listing_sessions_for_users'), + ] + + operations = [ + migrations.CreateModel( + name='PageFile', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('data_file', models.FileField(upload_to=onboarding.models.page_file_upload_to, validators=[onboarding.models.validate_file_extension])), + ('name', models.CharField(max_length=100)), + ('description', models.TextField(blank=True, help_text='Enter a description about file', max_length=700, null=True)), + ('size', models.DecimalField(decimal_places=2, max_digits=6)), + ('company', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='onboarding.company')), + ('page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='onboarding.page')), + ], + ), + ] diff --git a/onboarding/models.py b/onboarding/models.py index 95ef04d3..83ef1ec0 100644 --- a/onboarding/models.py +++ b/onboarding/models.py @@ -6,6 +6,7 @@ from django.contrib.auth.base_user import BaseUserManager from django.utils.translation import ugettext_lazy as _ from django.utils import timezone +from django.core.exceptions import ValidationError def upload_to(instance, filename): @@ -20,6 +21,36 @@ def upload_to(instance, filename): return f"users/{instance.pk}/{now:%Y%m%d%H%M%S}{milliseconds}{extension}" +def page_file_upload_to(instance, filename): + """ + :param instance: object of PageFile model + :param filename: name (with extension) of the file being uploaded + """ + company_id = instance.company + return f"company/{company_id}/{instance.page}/{filename}" + + +def validate_file_extension(value): + # valid_extensions = ['.doc', '.docx', '.pdf', '.jpg', '.xls', '.pptx'] + print(value) + print(value.file) + content_type = "" + if content_type in value.file: + content_type = value.file.content_type + whitelist = ['application/msword', 'application/pdf', 'application/vnd.ms-excel', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/rtf', 'application/vnd.ms-powerpoint', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'text/plain'] + else: + content_type = os.path.splitext(value.name)[1] + whitelist = ['.doc', '.docx', '.pdf', '.xls', '.pptx', '.jpg', '.png', '.gif', '.txt'] + + if not content_type in whitelist: + raise ValidationError('Unsupported file extension.') + + class Company(models.Model): company_logo = models.ImageField(upload_to=upload_to, null=True, blank=True) @@ -174,6 +205,26 @@ def __str__(self): return self.title +class PageFile(models.Model): + """ + Stores files for respective Page (many-to-one). + data_file - the file which is to be storred + name - keeps original name of the file + company - Company that owns this file + size - number of kilobytes of uploaded file + """ + data_file = models.FileField(upload_to=page_file_upload_to, validators=[validate_file_extension]) + page = models.ForeignKey(Page, on_delete=models.CASCADE) + name = models.CharField(max_length=100) + company = models.ForeignKey(Company, on_delete=models.CASCADE) + description = models.TextField(max_length=700, help_text='Enter a description about file', null=True, blank=True) + size = models.DecimalField(max_digits=6, decimal_places=2) + # updated_on = models.DateTimeField(auto_now=True) + + def __str__(self): + return f"file: {self.name}" + + class Section(models.Model): """ Owner - company where the HR/user who created the section is employed diff --git a/onboarding/serializers.py b/onboarding/serializers.py index d67ba1ef..3bbf8213 100644 --- a/onboarding/serializers.py +++ b/onboarding/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers from django.contrib.auth import get_user_model -from onboarding.models import ContactRequestDetail, Package, Page, Section, User, PackagesUsers +from onboarding.models import ContactRequestDetail, Package, Page, PageFile, Section, User, PackagesUsers from onboarding.models import Answer, Company, CompanyQuestionAndAnswer from . import mock_password @@ -291,6 +291,40 @@ class Meta: } +# FILES for PAGE +class PageFileSerializer(serializers.ModelSerializer): + class Meta: + model = PageFile + fields = '__all__' + read_only_fields = ('name', 'company', 'size') + + def validate(self, validated_data): + validated_data['name'] = validated_data['data_file'].name + validated_data['size'] = validated_data['data_file'].size / 1024.0 + return validated_data + + +#class PageFileMetaDataSerializer(serializers.ModelSerializer): +# class Meta: +# model = PageFile +# fields = ( +# 'id', +# 'page', +# 'name', +# 'company', +# 'description', +# 'size' +# ) +# read_only_fields = ('company', 'size') + + +class PageDataFileSerializer(serializers.ModelSerializer): + class Meta: + model = PageFile + fields = ('data_file',) + read_only_fields = ('data_file',) # [f.name for f in PageFile._meta.get_fields()] + + # PACKAGE with PAGEs class PackagePagesSerializer(serializers.ModelSerializer): page_set = PageSerializer(many=True) diff --git a/onboarding/urls.py b/onboarding/urls.py index fafb899b..105f319f 100644 --- a/onboarding/urls.py +++ b/onboarding/urls.py @@ -10,7 +10,7 @@ from .views import PackageViewSet, PageViewSet, SectionViewSet, UserViewSet, AnswerViewSet, CompanyViewSet, \ ContactFormViewSet, CompanyQuestionAndAnswerViewSet, UserAvatarUpload, UserProgressOnPageView,\ - UserProgressOnPackageView, PackagePagesViewSet, CustomPasswordResetConfirmView, \ + UserProgressOnPackageView, PackagePagesViewSet, CustomPasswordResetConfirmView, PageFileViewSet, \ WhenPackageSendToEmployeeView, PackagesUsersViewSet, CompanyLogoViewSet, UserProgressView, SectionAnswersViewSet # base @@ -71,6 +71,7 @@ router.register(r'api/contact_form', ContactFormViewSet, basename='contact_form') router.register(r'api/q_and_a', CompanyQuestionAndAnswerViewSet, basename='contact_form') +router.register(r'api/page_file', PageFileViewSet, basename='page_file') router.register(r'api/package_pages', PackagePagesViewSet, basename='PackagePages') # router.register(r'api/package_pages/list_by_company_employee', PackagePagesViewSet, basename='PackagePages') # router.register(r'api/package_pages//list_by_company_employee/', PackagePagesViewSet, basename='PackagePages') diff --git a/onboarding/views.py b/onboarding/views.py index 49495672..405f12bb 100644 --- a/onboarding/views.py +++ b/onboarding/views.py @@ -25,12 +25,13 @@ from OnlineOnboarding.settings import EMAIL_HOST_USER -from onboarding.models import Package, ContactRequestDetail, Page, Section, Answer, UserSessions +from onboarding.models import Package, ContactRequestDetail, Page, PageFile, Section, Answer, UserSessions from onboarding.models import User, Company, CompanyQuestionAndAnswer from .serializers import UserSerializer, UserAvatarSerializer, LogInUserSerializer, UserJobDataSerializer,\ UserUpdateSerializer, UsersListSerializer, UserProgressSerializer, UserProgressLimitedSerializer -from .serializers import PageSerializer, SectionSerializer, AnswersProgressStatusSerializer, PackageUsersSerializer +from .serializers import PageSerializer, PageFileSerializer, PageDataFileSerializer,\ + SectionSerializer, AnswersProgressStatusSerializer, PackageUsersSerializer # PageFileMetaDataSerializer,\ from .serializers import PackageSerializer, PackageForHrSerializer, PageSerializer, SectionSerializer,\ SectionAnswersSerializer, PackageAddUsersSerializer from .serializers import CompanyQuestionAndAnswerSerializer, PackagesUsers, ContactFormTestSerializer @@ -573,6 +574,45 @@ def list_by_package_employee(self, request, pk): return Response(serializer.data) + @action(detail=True, methods=['get'], serializer_class=PackagePagesForUsersSerializer) + def files(self, request, pk): + """ + :param request: user + :return: all files for Page + """ + files = PageFile.objects.filter(page=pk, company=self.request.user.company) + serializer = PageFileSerializer(files, many=True) + return Response(serializer.data) + + +class PageFileViewSet(viewsets.ModelViewSet): + """ + Model View Set for files of pages + """ + serializer_class = PageFileSerializer + permission_classes = [IsAuthenticated] + + def get_queryset(self): + user = self.request.user + + if user is not None: + queryset = PageFile.objects.filter(company=user.company) + else: + queryset = PageFile.objects.none() + return queryset + + def perform_create(self, serializer): + serializer.save(company=self.request.user.company) + + @action(detail=True) + def download(self, request, pk): + """ + :return: url path for file with id = pk + """ + data_file = PageFile.objects.get(pk=pk) + serializer = PageDataFileSerializer(data_file) + return Response(serializer.data) + class PackagePagesViewSet(viewsets.ModelViewSet): """ From bb09c150d001b7bb7acbc993b4414b606bf24bd0 Mon Sep 17 00:00:00 2001 From: Adam Sz <52573736+adam-sas-on@users.noreply.github.com> Date: Wed, 25 Aug 2021 15:25:41 +0200 Subject: [PATCH 03/15] Add content_type for downloading purposes DB can store many files with the same name, so this change enables downloading file with original type of the content --- .../0008_files_for_page_of_package.py | 7 ++--- onboarding/models.py | 26 ++++++++++++++----- onboarding/serializers.py | 6 ++++- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/onboarding/migrations/0008_files_for_page_of_package.py b/onboarding/migrations/0008_files_for_page_of_package.py index f278a768..d55537a8 100644 --- a/onboarding/migrations/0008_files_for_page_of_package.py +++ b/onboarding/migrations/0008_files_for_page_of_package.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.10 on 2021-08-24 11:37 +# Generated by Django 3.1.10 on 2021-08-25 11:34 from django.db import migrations, models import django.db.models.deletion @@ -18,10 +18,11 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('data_file', models.FileField(upload_to=onboarding.models.page_file_upload_to, validators=[onboarding.models.validate_file_extension])), ('name', models.CharField(max_length=100)), + ('content_type', models.CharField(blank=True, max_length=200, null=True)), ('description', models.TextField(blank=True, help_text='Enter a description about file', max_length=700, null=True)), ('size', models.DecimalField(decimal_places=2, max_digits=6)), - ('company', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='onboarding.company')), - ('page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='onboarding.page')), + ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='onboarding.company')), + ('page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='onboarding.page')), ], ), ] diff --git a/onboarding/models.py b/onboarding/models.py index 83ef1ec0..42b4ce8e 100644 --- a/onboarding/models.py +++ b/onboarding/models.py @@ -32,18 +32,21 @@ def page_file_upload_to(instance, filename): def validate_file_extension(value): # valid_extensions = ['.doc', '.docx', '.pdf', '.jpg', '.xls', '.pptx'] - print(value) - print(value.file) content_type = "" - if content_type in value.file: - content_type = value.file.content_type + try: + if content_type in value.file: + content_type = value.file.content_type + else: + content_type = value.content_type + whitelist = ['application/msword', 'application/pdf', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/rtf', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 'text/plain'] - else: + 'text/plain', + 'image/png', 'image/bmp', 'image/gif', 'image/jpe', 'image/jpeg', 'image/jpeg', 'image/svg+xml', 'image/x-icon'] + except (AttributeError, KeyError): content_type = os.path.splitext(value.name)[1] whitelist = ['.doc', '.docx', '.pdf', '.xls', '.pptx', '.jpg', '.png', '.gif', '.txt'] @@ -210,17 +213,28 @@ class PageFile(models.Model): Stores files for respective Page (many-to-one). data_file - the file which is to be storred name - keeps original name of the file + content_type - content type of data_file guessed by file extension by DJango company - Company that owns this file size - number of kilobytes of uploaded file """ data_file = models.FileField(upload_to=page_file_upload_to, validators=[validate_file_extension]) page = models.ForeignKey(Page, on_delete=models.CASCADE) name = models.CharField(max_length=100) + content_type = models.CharField(max_length=200, null=True, blank=True) company = models.ForeignKey(Company, on_delete=models.CASCADE) description = models.TextField(max_length=700, help_text='Enter a description about file', null=True, blank=True) size = models.DecimalField(max_digits=6, decimal_places=2) # updated_on = models.DateTimeField(auto_now=True) + # class Meta: + # constraints = [ + # models.UniqueConstraint(fields=['data_file', 'company'], name='unique file for each company') + # ] + + def delete(self): + self.data_file.storage.delete(self.data_file.name) + super().delete() + def __str__(self): return f"file: {self.name}" diff --git a/onboarding/serializers.py b/onboarding/serializers.py index 3bbf8213..eeee1ab0 100644 --- a/onboarding/serializers.py +++ b/onboarding/serializers.py @@ -296,9 +296,13 @@ class PageFileSerializer(serializers.ModelSerializer): class Meta: model = PageFile fields = '__all__' - read_only_fields = ('name', 'company', 'size') + read_only_fields = ('name', 'content_type', 'company', 'size') def validate(self, validated_data): + try: + validated_data['content_type'] = validated_data['data_file'].content_type + except (KeyError, AttributeError): + pass validated_data['name'] = validated_data['data_file'].name validated_data['size'] = validated_data['data_file'].size / 1024.0 return validated_data From bfb967897ef01d0b19b48e45ebf8af762e891e06 Mon Sep 17 00:00:00 2001 From: Adam Sz <52573736+adam-sas-on@users.noreply.github.com> Date: Wed, 25 Aug 2021 15:29:04 +0200 Subject: [PATCH 04/15] Fix downloading endpoint for missing files case and enable raw downloading Raw downloading is for cases when files have originally the same name but another on the server Apply python's "It's easier to ask for forgiveness than permission" (EAFP) --- onboarding/views.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/onboarding/views.py b/onboarding/views.py index 405f12bb..7ab86106 100644 --- a/onboarding/views.py +++ b/onboarding/views.py @@ -609,9 +609,29 @@ def download(self, request, pk): """ :return: url path for file with id = pk """ - data_file = PageFile.objects.get(pk=pk) - serializer = PageDataFileSerializer(data_file) - return Response(serializer.data) + try: + data_file = PageFile.objects.get(pk=pk) + except PageFile.DoesNotExist: + return Response(status=status.HTTP_404_NOT_FOUND) + + try: + file_open = data_file.data_file.storage.open(data_file.data_file.name) + except AttributeError: + return Response(status=status.HTTP_404_NOT_FOUND) + + content_type = "" + try: + content_type = data_file.content_type + except AttributeError: + pass + + response = HttpResponse(file_open, content_type=content_type) + try: + response['Content-Length'] = data_file.data_file.size + except AttributeError: + response['Content-Length'] = data_file.size * 1024 + response['Content-Disposition'] = f"attachment; filename={data_file.name}" + return response class PackagePagesViewSet(viewsets.ModelViewSet): From dfcc34466012fd681eb43be93b9f1dc7c7c5d428 Mon Sep 17 00:00:00 2001 From: Adam Sz <52573736+adam-sas-on@users.noreply.github.com> Date: Wed, 25 Aug 2021 18:02:55 +0200 Subject: [PATCH 05/15] Add functions to request a list of files and to delete specific file; #160 --- onboarding/src/Components/hooks/FormsEdit.js | 58 ++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/onboarding/src/Components/hooks/FormsEdit.js b/onboarding/src/Components/hooks/FormsEdit.js index 6d34dbf6..f0b4d2d9 100644 --- a/onboarding/src/Components/hooks/FormsEdit.js +++ b/onboarding/src/Components/hooks/FormsEdit.js @@ -102,5 +102,63 @@ export function savePageDetails(handleSuccess, pageId, title, link, description) return true; } +/** + * Requests files for page and works on them by callback function + * + * @param pageId - an id of page for which we want to reqests and get files; + * @param setterForFilesState - callback function to call when success; + * @param showErrorCallback - callback function to show an error of request; + * @returns {*} - abort object fro Promise-fetch; + */ +export function getFilesForPage(pageId, setterForFilesState, showErrorCallback){ + let fetchProps = {method:"GET", headers:{"Accept":"application/json", "Content-Type":"application/json", "X-CSRFToken":""}}, + abortCont = new AbortController(); + var url = getPath(); + fetchProps.signal = abortCont.signal; + + fetch(url + "api/page/" + pageId + "/files/", fetchProps) + .then(res => { + if(!res.ok) + throw Error("Wystąpił błąd podczas pobierania listy plików!"); + return res.json(); + }) + .then( (response) => { + let files = [], row, downloadUrl = url + "api/page_file/";// 1/download/"; + response.forEach(function(element){ + row = element; + row.data_file = downloadUrl + element.id + "/download/"; + files.push(row); + }); + setterForFilesState(files); + }) + .catch(error => { + showErrorCallback(error.message); + }); + + return abortCont; +} + +/** + * Removes file of pages in packages. + * @param pageFileId - id of file to be removed; + * @param removeCallback - callback function to be called when request is finished; + * @returns {*}; + */ +export function removePageFile(pageFileId, removeCallback){ + let url = getPath(), token = getCookie('csrftoken'), + fetchProps = {method:"DELETE", headers:{"Accept":"application/json", "Content-Type":"application/json", "X-CSRFToken":token}}; + + fetch(url + "api/page_file/" + pageFileId + "/", fetchProps) + .then(res => { + if(!res.ok) + throw Error("Błąd: nie udało się usunąć pliku!"); + return (res.status !== 204) ? res.json() : res;// 204: HTTP_204_NO_CONTENT; + }).then(data => { + removeCallback("Udało się usunąć plik.", pageFileId); + }).catch(err => { + removeCallback(err.message); + }); +} + export default FormsEdit; From eb910ff1c71cdda6c1f73c4931fae4df5e0095a6 Mon Sep 17 00:00:00 2001 From: Adam Sz <52573736+adam-sas-on@users.noreply.github.com> Date: Wed, 25 Aug 2021 20:43:31 +0200 Subject: [PATCH 06/15] Add loading files of page into table and requesting a deletion of file(s) with confirmation on modal; #160 --- .../Components/FormsEdit/FormDescription.js | 77 ++++++++++++++++++- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/onboarding/src/Components/FormsEdit/FormDescription.js b/onboarding/src/Components/FormsEdit/FormDescription.js index ebde81d8..080704ad 100644 --- a/onboarding/src/Components/FormsEdit/FormDescription.js +++ b/onboarding/src/Components/FormsEdit/FormDescription.js @@ -1,9 +1,11 @@ import React, { useEffect, useRef, useState } from "react"; import { useLocation } from "react-router-dom"; -import { savePageDetails } from "../hooks/FormsEdit"; +import { savePageDetails, getFilesForPage, removePageFile } from "../hooks/FormsEdit"; import ModalWarning from "../ModalWarning"; import { isValidUrl } from "../utils"; import bookOpenedIcon from "../../static/icons/book-opened.svg"; +import trashIcon from "../../static/icons/trash.svg"; + const FormDescription = ({ formId, formData }) => { const formFilesRef = useRef([]); @@ -25,10 +27,41 @@ const FormDescription = ({ formId, formData }) => { formData.title && setFormName(formData.title); formData.description && setDescription(formData.description); formData.link && setLink(formData.link); - }; - }; + } + } }, [formData]); + useEffect(() => { + let abortCont = getFilesForPage(formId, arrayOfFilesToTable, arrayOfFilesToTable); + return () => abortCont.abort(); + }, [formId]); + + + const arrayOfFilesToTable = (files) => { + let tableFiles = []; + if(typeof files === 'string' || files instanceof String){ + tableFiles.push({ files }); + updateFormFiles(tableFiles); + return; + } + + let row, button, link; + files.forEach((element) => { + button = + link = { element.name } + row = { link }{ element.description }{ element.size } kB{ button } + tableFiles.push(row); + }); + updateFormFiles(tableFiles); + }; + + const updateFiles = function(fileId){ + setSaveModal(<>>); + + if(fileId > 0) + getFilesForPage(formId, arrayOfFilesToTable, arrayOfFilesToTable); + }; + const hideModal = () => { setSaveModal(<>>); }; @@ -46,6 +79,34 @@ const FormDescription = ({ formId, formData }) => { ); }; + const popUpAskForDeleteFile = function(e){ + e.preventDefault(); + setSaveModal( + + ); + }; + + const popUpDeleteFileInformation = (message, fileId) => { + setSaveModal( + + ); + }; + const handleSave = (e) => { e.preventDefault(); const isValid = isValidUrl(link); @@ -62,6 +123,11 @@ const FormDescription = ({ formId, formData }) => { } }; + const handleRemoveFile = (fileId) => { + removePageFile(fileId, popUpDeleteFileInformation); + }; + + return ( @@ -103,6 +169,11 @@ const FormDescription = ({ formId, formData }) => { Dołącz plik {e.preventDefault()}} />{/* Dołącz plik */} + { formFiles.length > 0 && ( + + { formFiles } + + ) } Tekst (liczba znaków: 1500) Date: Tue, 31 Aug 2021 13:05:06 +0200 Subject: [PATCH 07/15] Restyle the list of files --- .../src/Components/FormsEdit/FormDescription.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/onboarding/src/Components/FormsEdit/FormDescription.js b/onboarding/src/Components/FormsEdit/FormDescription.js index 080704ad..8123827b 100644 --- a/onboarding/src/Components/FormsEdit/FormDescription.js +++ b/onboarding/src/Components/FormsEdit/FormDescription.js @@ -40,16 +40,16 @@ const FormDescription = ({ formId, formData }) => { const arrayOfFilesToTable = (files) => { let tableFiles = []; if(typeof files === 'string' || files instanceof String){ - tableFiles.push({ files }); + tableFiles.push({ files }); updateFormFiles(tableFiles); return; } let row, button, link; files.forEach((element) => { - button = - link = { element.name } - row = { link }{ element.description }{ element.size } kB{ button } + button = + link = { element.name } ({ element.size } kB) + row = { link } | { button } tableFiles.push(row); }); updateFormFiles(tableFiles); @@ -170,9 +170,9 @@ const FormDescription = ({ formId, formData }) => { {e.preventDefault()}} />{/* Dołącz plik */} { formFiles.length > 0 && ( - + { formFiles } - + ) } Tekst (liczba znaków: 1500) Date: Wed, 1 Sep 2021 16:16:01 +0200 Subject: [PATCH 08/15] Add function to upload files for page; #160 Use XMLHttpRequest to make possible to use progress --- onboarding/src/Components/hooks/FormsEdit.js | 76 ++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/onboarding/src/Components/hooks/FormsEdit.js b/onboarding/src/Components/hooks/FormsEdit.js index f0b4d2d9..1300cd89 100644 --- a/onboarding/src/Components/hooks/FormsEdit.js +++ b/onboarding/src/Components/hooks/FormsEdit.js @@ -138,6 +138,82 @@ export function getFilesForPage(pageId, setterForFilesState, showErrorCallback){ return abortCont; } +/** + * Requests files for page and works on them by callback function + * + * @param pageId - an id of page for which we want to reqests and get files; + * @param files - + * @param showResult - + * @param showError - + * @param showProgress - + * @returns {*} - abort function for all requests for files; + */ +export function addNewFiles(pageId, files, showResult, showError, showProgress){ + var xhrsAll = []; + let xhr, url, token = getCookie('csrftoken'); + + url = getPath(); + url = url + "api/page_file/"; + + let i, count = files.length, formData; + + for(i = 0; i < count; i++){ + xhr = new XMLHttpRequest(); + formData = new FormData(); + formData.append("data_file", files[i]); + formData.append("page", pageId); + formData.append("description", ""); + let fileName = files[i].name; + //formData.append("csrftoken": token); + + xhr.onreadystatechange = function(){ + if(this.readyState == 4 && this.status >= 200 && this.status < 300){ + let response; + try { + response = JSON.parse(this.responseText); + }catch(e){ + response = "";} + showResult(i, response); + + } else if(this.readyState == 4){ + showError(i, this.responseText, this.status);// this.status >= 400 ...; + } + }; + xhr.onprogress = function(e){ + if(e.lengthComputable){ + showProgress(fileName, e.loaded, e.total); + } + } + + xhr.open("POST", url, true);/* async: true (asynchronous) or false (synchronous); */ + + xhr.setRequestHeader("Accept", "application/json"); + //xhr.setRequestHeader("Content-Type", "multipart/form-data");/ / "Multipart form parse error - Invalid boundary in multipart: None"!!! + xhr.setRequestHeader("X-CSRFToken", token); + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + xhrsAll.push(xhr); + + xhr.send(formData); + } + + //await Promise.all(files.map((file) => { + // let formData = new FormData(); + // formData.append("data_file", file); + // formData.append("page": pageId); + // //formData.append("csrftoken": token); + // fetchProps.body = formData; + + // return fetch(url, fetchProps); + // } + //)); + + return function(){ + let i = xhrsAll.length; + for(; i >= 0; i--) + xhrsAll.abort(); + }; +} + /** * Removes file of pages in packages. * @param pageFileId - id of file to be removed; From dfe4c8d79aa2926b926a75062d5e4380b44e2316 Mon Sep 17 00:00:00 2001 From: Adam Sz <52573736+adam-sas-on@users.noreply.github.com> Date: Wed, 1 Sep 2021 17:04:40 +0200 Subject: [PATCH 09/15] Implement management of files for a page; #160 - Opening - Removing - Uploading Include showing progress (simplified version) --- .../Components/FormsEdit/FormDescription.js | 98 ++++++++++++++++--- 1 file changed, 87 insertions(+), 11 deletions(-) diff --git a/onboarding/src/Components/FormsEdit/FormDescription.js b/onboarding/src/Components/FormsEdit/FormDescription.js index 8123827b..f4876a70 100644 --- a/onboarding/src/Components/FormsEdit/FormDescription.js +++ b/onboarding/src/Components/FormsEdit/FormDescription.js @@ -1,6 +1,6 @@ import React, { useEffect, useRef, useState } from "react"; import { useLocation } from "react-router-dom"; -import { savePageDetails, getFilesForPage, removePageFile } from "../hooks/FormsEdit"; +import { savePageDetails, getFilesForPage, removePageFile, addNewFiles } from "../hooks/FormsEdit"; import ModalWarning from "../ModalWarning"; import { isValidUrl } from "../utils"; import bookOpenedIcon from "../../static/icons/book-opened.svg"; @@ -8,13 +8,16 @@ import trashIcon from "../../static/icons/trash.svg"; const FormDescription = ({ formId, formData }) => { - const formFilesRef = useRef([]); + const formFilesRef = useRef(); const location = useLocation(); const [formName, setFormName] = useState(""); const [link, setLink] = useState(""); const [description, setDescription] = useState(""); const [saveModal, setSaveModal ] = useState(<>>); const [formFiles, updateFormFiles] = useState([]); + const [filesToSend, appendFileToSend] = useState([]); + const [filesToSendTable, updateFileToSendTable] = useState([]); + const [uploadingFilesProgress, updateUploadingProgress] = useState(true); useEffect(() => { @@ -32,9 +35,11 @@ const FormDescription = ({ formId, formData }) => { }, [formData]); useEffect(() => { - let abortCont = getFilesForPage(formId, arrayOfFilesToTable, arrayOfFilesToTable); - return () => abortCont.abort(); - }, [formId]); + if(uploadingFilesProgress === true){ + let abortCont = getFilesForPage(formId, arrayOfFilesToTable, arrayOfFilesToTable); + return () => abortCont.abort(); + } + }, [formId, uploadingFilesProgress]); const arrayOfFilesToTable = (files) => { @@ -55,6 +60,19 @@ const FormDescription = ({ formId, formData }) => { updateFormFiles(tableFiles); }; + const openedFilesToTable = (openedFiles) => { + let openedFilesDOM = [], rmButton; + openedFiles.forEach((file, index) => { + if(!file.name || !file.size) + return; + + rmButton = + openedFilesDOM.push({ file.name } ({ (file.size/1024.0).toFixed(2) } kB) | { rmButton }); + }); + updateFileToSendTable(openedFilesDOM); + }; + + const updateFiles = function(fileId){ setSaveModal(<>>); @@ -62,6 +80,29 @@ const FormDescription = ({ formId, formData }) => { getFilesForPage(formId, arrayOfFilesToTable, arrayOfFilesToTable); }; + const openFile = function(e){ + e.preventDefault(); + if(typeof formFilesRef.current.files !== 'undefined' && formFilesRef.current.files.length > 0){ + let newFilesList = [...filesToSend, formFilesRef.current.files[0]]; + appendFileToSend(newFilesList); + openedFilesToTable(newFilesList); + } + }; + + const removeFromOpened = (e) => { + e.preventDefault(); + let fileName = e.target.value; + if(typeof fileName === 'undefined')// when is clicked, this happen; + fileName = e.target.parentNode.value; + + if(typeof fileName !== 'undefined'){ + let newFilesList = filesToSend.filter((file) => file.name !== fileName); + appendFileToSend(newFilesList); + openedFilesToTable(newFilesList); + } + }; + + const hideModal = () => { setSaveModal(<>>); }; @@ -81,6 +122,10 @@ const FormDescription = ({ formId, formData }) => { const popUpAskForDeleteFile = function(e){ e.preventDefault(); + let fileId = e.target.value; + if(typeof fileId === 'undefined')// when is clicked, this happen; + fileId = e.target.parentNode.value; + setSaveModal( { message={ "Czy na pewno chcesz by plik został usunięty?" } show={true} acceptText={"Ok"} - id={ parseInt(e.target.value, 10) } + id={ parseInt(fileId, 10) } /> ); }; @@ -119,7 +164,16 @@ const FormDescription = ({ formId, formData }) => { description ); // pack as one argument; } else { - popUpSaveFormDetails("Błąd: Wprowadzono nieprawidłowy adres url") + popUpSaveFormDetails("Błąd: Wprowadzono nieprawidłowy adres url"); + } + + if(filesToSend.length > 0){ + let filesToSendCopy = []; + for(let i = 0; i < filesToSend.length; i++) + filesToSendCopy.push(filesToSend[i]); + + addNewFiles(formId, filesToSendCopy, function(){}, function(){}, showProgress); + showProgress(false); } }; @@ -127,6 +181,27 @@ const FormDescription = ({ formId, formData }) => { removePageFile(fileId, popUpDeleteFileInformation); }; + const showProgress = function(fileIndex, loaded, totalBytes){ + if(fileIndex === false){ + updateFileToSendTable([]); + return; + } + + let progressCopy = {}; + if(uploadingFilesProgress !== true) + progressCopy = JSON.parse(JSON.stringify(uploadingFilesProgress) ) + + progressCopy[fileIndex] = {loaded: loaded, total: totalBytes}; + let filesToSendNewTable = [], percentage; + Object.keys(progressCopy).forEach( (fileName) => { + percentage = progressCopy[fileName].loaded / progressCopy[fileName].total * 100.0; + percentage = parseFloat(percentage).toFixed(2); + filesToSendNewTable.push({ fileName } | { percentage }%); + }); + updateFileToSendTable(filesToSendNewTable); + }; + + return ( @@ -155,6 +230,7 @@ const FormDescription = ({ formId, formData }) => { Link do wideo / dokumentu + {/* (Limit na rozdział: 10MB - potrzebujesz więcej? < a >Dowiedz się jak... a >) */} { onChange={ (e) => setLink(e.target.value) } maxLength="200" /> - {/* TODO: funkcjonalność przycisku */} + Dołącz plik - {e.preventDefault()}} />{/* Dołącz plik */} + {/* Dołącz plik */} - { formFiles.length > 0 && ( + { filesToSendTable } { formFiles } - ) } + Tekst (liczba znaków: 1500) Date: Sun, 5 Sep 2021 14:04:49 +0200 Subject: [PATCH 10/15] Add showing progress of uploading --- .../Components/FormsEdit/FormDescription.js | 59 +++++++++++++++---- onboarding/src/Components/hooks/FormsEdit.js | 2 +- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/onboarding/src/Components/FormsEdit/FormDescription.js b/onboarding/src/Components/FormsEdit/FormDescription.js index f4876a70..1cf0e111 100644 --- a/onboarding/src/Components/FormsEdit/FormDescription.js +++ b/onboarding/src/Components/FormsEdit/FormDescription.js @@ -152,6 +152,11 @@ const FormDescription = ({ formId, formData }) => { ); }; + const handleRemoveFile = (fileId) => { + removePageFile(fileId, popUpDeleteFileInformation); + }; + + const handleSave = (e) => { e.preventDefault(); const isValid = isValidUrl(link); @@ -172,34 +177,64 @@ const FormDescription = ({ formId, formData }) => { for(let i = 0; i < filesToSend.length; i++) filesToSendCopy.push(filesToSend[i]); - addNewFiles(formId, filesToSendCopy, function(){}, function(){}, showProgress); + addNewFiles(formId, filesToSendCopy, messageWhenOneFileUploaded, function(){}, showProgress); showProgress(false); } }; - const handleRemoveFile = (fileId) => { - removePageFile(fileId, popUpDeleteFileInformation); - }; - const showProgress = function(fileIndex, loaded, totalBytes){ + let filesToSendNewTable = []; if(fileIndex === false){ - updateFileToSendTable([]); + let fName; + for(let i = 0; i < filesToSend.length; i++){ + fName = filesToSend[i].name; + filesToSendNewTable.push({ fName } | 0.0%); + } + + updateFileToSendTable(filesToSendNewTable); return; } - let progressCopy = {}; + let progressCopy = {};console.log(uploadingFilesProgress); if(uploadingFilesProgress !== true) - progressCopy = JSON.parse(JSON.stringify(uploadingFilesProgress) ) + progressCopy = JSON.parse(JSON.stringify(uploadingFilesProgress) ); + progressCopy[fileIndex] = {loaded: loaded, total: totalBytes}; - let filesToSendNewTable = [], percentage; - Object.keys(progressCopy).forEach( (fileName) => { + let percentage = percentage = loaded / totalBytes * 100.0; + filesToSendNewTable = filesToSendTable.map((fileJSX) => { + if(fileJSX.file === fileIndex) + return { fileIndex } | { percentage }% + + return fileJSX; + }); +console.log("progressCopy: ", progressCopy, filesToSendNewTable); + + /*Object.keys(progressCopy).forEach( (fileName) => { percentage = progressCopy[fileName].loaded / progressCopy[fileName].total * 100.0; percentage = parseFloat(percentage).toFixed(2); filesToSendNewTable.push({ fileName } | { percentage }%); - }); - updateFileToSendTable(filesToSendNewTable); + });*/ + updateFileToSendTable(filesToSendNewTable); + updateUploadingProgress(progressCopy); + }; + + const messageWhenOneFileUploaded = function(fileName, response){ + let button = + let link = { response.name } ({ (response.size/1024.0).toFixed(2) } kB) + let row = { link } | { button } + + formFiles.push(row); + updateFormFiles(formFiles); + + let progressCopy = JSON.parse(JSON.stringify(uploadingFilesProgress) ); + + if(progressCopy.hasOwnProperty(fileName) ) + delete progressCopy[fileName]; +console.log(progressCopy); + updateUploadingProgress(progressCopy); }; + diff --git a/onboarding/src/Components/hooks/FormsEdit.js b/onboarding/src/Components/hooks/FormsEdit.js index 1300cd89..6b399b62 100644 --- a/onboarding/src/Components/hooks/FormsEdit.js +++ b/onboarding/src/Components/hooks/FormsEdit.js @@ -173,7 +173,7 @@ export function addNewFiles(pageId, files, showResult, showError, showProgress){ response = JSON.parse(this.responseText); }catch(e){ response = "";} - showResult(i, response); + showResult(fileName, response); } else if(this.readyState == 4){ showError(i, this.responseText, this.status);// this.status >= 400 ...; From 99e06d81a42dc584204b31c01e5658f52e38b29f Mon Sep 17 00:00:00 2001 From: Adam Sz <52573736+adam-sas-on@users.noreply.github.com> Date: Tue, 14 Sep 2021 13:14:08 +0200 Subject: [PATCH 11/15] Add helper comment, remove console logs --- onboarding/src/Components/FormsEdit/FormDescription.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/onboarding/src/Components/FormsEdit/FormDescription.js b/onboarding/src/Components/FormsEdit/FormDescription.js index 1cf0e111..8a249f6c 100644 --- a/onboarding/src/Components/FormsEdit/FormDescription.js +++ b/onboarding/src/Components/FormsEdit/FormDescription.js @@ -17,7 +17,7 @@ const FormDescription = ({ formId, formData }) => { const [formFiles, updateFormFiles] = useState([]); const [filesToSend, appendFileToSend] = useState([]); const [filesToSendTable, updateFileToSendTable] = useState([]); - const [uploadingFilesProgress, updateUploadingProgress] = useState(true); + const [uploadingFilesProgress, updateUploadingProgress] = useState(true);// array of progresses of files or true if there is no files and list can be updated; useEffect(() => { @@ -195,7 +195,7 @@ const FormDescription = ({ formId, formData }) => { return; } - let progressCopy = {};console.log(uploadingFilesProgress); + let progressCopy = {}; if(uploadingFilesProgress !== true) progressCopy = JSON.parse(JSON.stringify(uploadingFilesProgress) ); @@ -208,7 +208,6 @@ const FormDescription = ({ formId, formData }) => { return fileJSX; }); -console.log("progressCopy: ", progressCopy, filesToSendNewTable); /*Object.keys(progressCopy).forEach( (fileName) => { percentage = progressCopy[fileName].loaded / progressCopy[fileName].total * 100.0; @@ -231,7 +230,7 @@ console.log("progressCopy: ", progressCopy, filesToSendNewTable); if(progressCopy.hasOwnProperty(fileName) ) delete progressCopy[fileName]; -console.log(progressCopy); + updateUploadingProgress(progressCopy); }; From 8b3c575690c8b097cad9cdacfcc69b6aaf23ec2b Mon Sep 17 00:00:00 2001 From: Adam Sz <52573736+adam-sas-on@users.noreply.github.com> Date: Wed, 15 Sep 2021 11:31:18 +0200 Subject: [PATCH 12/15] Try solution with progress as a number of files during uploading --- .../Components/FormsEdit/FormDescription.js | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/onboarding/src/Components/FormsEdit/FormDescription.js b/onboarding/src/Components/FormsEdit/FormDescription.js index 8a249f6c..33ed5681 100644 --- a/onboarding/src/Components/FormsEdit/FormDescription.js +++ b/onboarding/src/Components/FormsEdit/FormDescription.js @@ -17,7 +17,7 @@ const FormDescription = ({ formId, formData }) => { const [formFiles, updateFormFiles] = useState([]); const [filesToSend, appendFileToSend] = useState([]); const [filesToSendTable, updateFileToSendTable] = useState([]); - const [uploadingFilesProgress, updateUploadingProgress] = useState(true);// array of progresses of files or true if there is no files and list can be updated; + const [uploadingFilesProgress, updateUploadingProgress] = useState(0);// array of progresses of files or true if there is no files and list can be updated; useEffect(() => { @@ -35,7 +35,7 @@ const FormDescription = ({ formId, formData }) => { }, [formData]); useEffect(() => { - if(uploadingFilesProgress === true){ + if(uploadingFilesProgress === 0){ let abortCont = getFilesForPage(formId, arrayOfFilesToTable, arrayOfFilesToTable); return () => abortCont.abort(); } @@ -177,34 +177,36 @@ const FormDescription = ({ formId, formData }) => { for(let i = 0; i < filesToSend.length; i++) filesToSendCopy.push(filesToSend[i]); - addNewFiles(formId, filesToSendCopy, messageWhenOneFileUploaded, function(){}, showProgress); showProgress(false); + addNewFiles(formId, filesToSendCopy, messageWhenOneFileUploaded, function(){}, showProgress); } }; const showProgress = function(fileIndex, loaded, totalBytes){ - let filesToSendNewTable = []; + let filesToSendNewTable = [], progressCopy = {}; if(fileIndex === false){ let fName; for(let i = 0; i < filesToSend.length; i++){ fName = filesToSend[i].name; + progressCopy[fName] = {loaded: 0, total: 0};// filesToSend[i].size; filesToSendNewTable.push({ fName } | 0.0%); } + updateUploadingProgress(filesToSend.length); updateFileToSendTable(filesToSendNewTable); return; } - let progressCopy = {}; - if(uploadingFilesProgress !== true) - progressCopy = JSON.parse(JSON.stringify(uploadingFilesProgress) ); + //if(uploadingFilesProgress !== true) + // progressCopy = JSON.parse(JSON.stringify(uploadingFilesProgress) ); progressCopy[fileIndex] = {loaded: loaded, total: totalBytes}; +updateUploadingProgress(progressCopy); let percentage = percentage = loaded / totalBytes * 100.0; filesToSendNewTable = filesToSendTable.map((fileJSX) => { if(fileJSX.file === fileIndex) - return { fileIndex } | { percentage }% + return percentage < 100.0 ? { fileIndex } | { percentage }% : <>> return fileJSX; }); @@ -215,7 +217,7 @@ const FormDescription = ({ formId, formData }) => { filesToSendNewTable.push({ fileName } | { percentage }%); });*/ updateFileToSendTable(filesToSendNewTable); - updateUploadingProgress(progressCopy); + //updateUploadingProgress(progressCopy); }; const messageWhenOneFileUploaded = function(fileName, response){ @@ -228,10 +230,9 @@ const FormDescription = ({ formId, formData }) => { let progressCopy = JSON.parse(JSON.stringify(uploadingFilesProgress) ); - if(progressCopy.hasOwnProperty(fileName) ) - delete progressCopy[fileName]; - - updateUploadingProgress(progressCopy); + //if(progressCopy.hasOwnProperty(fileName) ) + // delete progressCopy[fileName]; + updateUploadingProgress(uploadingFilesProgress - 1); }; From 600ca9a9f6b28c2509694d6ab1516240108f8b47 Mon Sep 17 00:00:00 2001 From: Adam Sz <52573736+adam-sas-on@users.noreply.github.com> Date: Wed, 15 Sep 2021 15:23:25 +0200 Subject: [PATCH 13/15] Move progress-to-view processing into useEffect() to ensure files uploading synchronization --- .../Components/FormsEdit/FormDescription.js | 60 +++++++++---------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/onboarding/src/Components/FormsEdit/FormDescription.js b/onboarding/src/Components/FormsEdit/FormDescription.js index 33ed5681..ab91e179 100644 --- a/onboarding/src/Components/FormsEdit/FormDescription.js +++ b/onboarding/src/Components/FormsEdit/FormDescription.js @@ -17,7 +17,7 @@ const FormDescription = ({ formId, formData }) => { const [formFiles, updateFormFiles] = useState([]); const [filesToSend, appendFileToSend] = useState([]); const [filesToSendTable, updateFileToSendTable] = useState([]); - const [uploadingFilesProgress, updateUploadingProgress] = useState(0);// array of progresses of files or true if there is no files and list can be updated; + const [uploadingFilesProgress, updateUploadingProgress] = useState(true);// array of progresses of files or true if there is no files and list can be updated; useEffect(() => { @@ -35,9 +35,20 @@ const FormDescription = ({ formId, formData }) => { }, [formData]); useEffect(() => { - if(uploadingFilesProgress === 0){ + if(uploadingFilesProgress === true){ let abortCont = getFilesForPage(formId, arrayOfFilesToTable, arrayOfFilesToTable); + updateFileToSendTable([]);// remove list of un-uploaded files; return () => abortCont.abort(); + } else if(updateUploadingProgress && Object.keys(updateUploadingProgress).length > 0){ + + let filesToSendNewTable = [], percentage; + Object.keys(updateUploadingProgress).forEach( (fileName) => { + percentage = progressCopy[fileName].loaded / progressCopy[fileName].total * 100.0; + percentage = parseFloat(percentage).toFixed(2); + filesToSendNewTable.push({ fileName } | { percentage }%); + }); + + updateFileToSendTable(filesToSendNewTable); } }, [formId, uploadingFilesProgress]); @@ -177,47 +188,30 @@ const FormDescription = ({ formId, formData }) => { for(let i = 0; i < filesToSend.length; i++) filesToSendCopy.push(filesToSend[i]); - showProgress(false); + showProgress(); addNewFiles(formId, filesToSendCopy, messageWhenOneFileUploaded, function(){}, showProgress); } }; - const showProgress = function(fileIndex, loaded, totalBytes){ - let filesToSendNewTable = [], progressCopy = {}; - if(fileIndex === false){ + const showProgress = function(fileName, loaded, totalBytes){ + let progressCopy = {}; + if(fileName === undefined || fileName === null){ let fName; for(let i = 0; i < filesToSend.length; i++){ fName = filesToSend[i].name; progressCopy[fName] = {loaded: 0, total: 0};// filesToSend[i].size; - filesToSendNewTable.push({ fName } | 0.0%); } - updateUploadingProgress(filesToSend.length); - updateFileToSendTable(filesToSendNewTable); + updateUploadingProgress(progressCopy); return; } - //if(uploadingFilesProgress !== true) - // progressCopy = JSON.parse(JSON.stringify(uploadingFilesProgress) ); + if(uploadingFilesProgress !== true) + progressCopy = JSON.parse(JSON.stringify(uploadingFilesProgress) ); - progressCopy[fileIndex] = {loaded: loaded, total: totalBytes}; -updateUploadingProgress(progressCopy); - let percentage = percentage = loaded / totalBytes * 100.0; - filesToSendNewTable = filesToSendTable.map((fileJSX) => { - if(fileJSX.file === fileIndex) - return percentage < 100.0 ? { fileIndex } | { percentage }% : <>> - - return fileJSX; - }); - - /*Object.keys(progressCopy).forEach( (fileName) => { - percentage = progressCopy[fileName].loaded / progressCopy[fileName].total * 100.0; - percentage = parseFloat(percentage).toFixed(2); - filesToSendNewTable.push({ fileName } | { percentage }%); - });*/ - updateFileToSendTable(filesToSendNewTable); - //updateUploadingProgress(progressCopy); + progressCopy[fileName] = {loaded: loaded, total: totalBytes}; + updateUploadingProgress(progressCopy); }; const messageWhenOneFileUploaded = function(fileName, response){ @@ -230,9 +224,13 @@ updateUploadingProgress(progressCopy); let progressCopy = JSON.parse(JSON.stringify(uploadingFilesProgress) ); - //if(progressCopy.hasOwnProperty(fileName) ) - // delete progressCopy[fileName]; - updateUploadingProgress(uploadingFilesProgress - 1); + if(progressCopy.hasOwnProperty(fileName) ) + delete progressCopy[fileName]; + + if(Object.keys(progressCopy).length < 1) + updateUploadingProgress(true); + else + updateUploadingProgress(progressCopy); }; From 30cb0b654e3a6b78a619678283b857fc695a8557 Mon Sep 17 00:00:00 2001 From: Adam Sz <52573736+adam-sas-on@users.noreply.github.com> Date: Fri, 17 Sep 2021 15:45:36 +0200 Subject: [PATCH 14/15] Fix duplicate files after resend --- onboarding/src/Components/FormsEdit/FormDescription.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/onboarding/src/Components/FormsEdit/FormDescription.js b/onboarding/src/Components/FormsEdit/FormDescription.js index ab91e179..08857a35 100644 --- a/onboarding/src/Components/FormsEdit/FormDescription.js +++ b/onboarding/src/Components/FormsEdit/FormDescription.js @@ -36,8 +36,8 @@ const FormDescription = ({ formId, formData }) => { useEffect(() => { if(uploadingFilesProgress === true){ - let abortCont = getFilesForPage(formId, arrayOfFilesToTable, arrayOfFilesToTable); updateFileToSendTable([]);// remove list of un-uploaded files; + let abortCont = getFilesForPage(formId, arrayOfFilesToTable, arrayOfFilesToTable); return () => abortCont.abort(); } else if(updateUploadingProgress && Object.keys(updateUploadingProgress).length > 0){ @@ -190,6 +190,7 @@ const FormDescription = ({ formId, formData }) => { showProgress(); addNewFiles(formId, filesToSendCopy, messageWhenOneFileUploaded, function(){}, showProgress); + appendFileToSend([]); } }; From 8457d4425800ee676323891a76d37139d9368193 Mon Sep 17 00:00:00 2001 From: Adam Sz <52573736+adam-sas-on@users.noreply.github.com> Date: Fri, 17 Sep 2021 16:34:30 +0200 Subject: [PATCH 15/15] Comment obsolete description --- onboarding/migrations/0008_files_for_page_of_package.py | 1 - onboarding/models.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/onboarding/migrations/0008_files_for_page_of_package.py b/onboarding/migrations/0008_files_for_page_of_package.py index d55537a8..9f0d330e 100644 --- a/onboarding/migrations/0008_files_for_page_of_package.py +++ b/onboarding/migrations/0008_files_for_page_of_package.py @@ -19,7 +19,6 @@ class Migration(migrations.Migration): ('data_file', models.FileField(upload_to=onboarding.models.page_file_upload_to, validators=[onboarding.models.validate_file_extension])), ('name', models.CharField(max_length=100)), ('content_type', models.CharField(blank=True, max_length=200, null=True)), - ('description', models.TextField(blank=True, help_text='Enter a description about file', max_length=700, null=True)), ('size', models.DecimalField(decimal_places=2, max_digits=6)), ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='onboarding.company')), ('page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='onboarding.page')), diff --git a/onboarding/models.py b/onboarding/models.py index 42b4ce8e..4318066f 100644 --- a/onboarding/models.py +++ b/onboarding/models.py @@ -222,7 +222,7 @@ class PageFile(models.Model): name = models.CharField(max_length=100) content_type = models.CharField(max_length=200, null=True, blank=True) company = models.ForeignKey(Company, on_delete=models.CASCADE) - description = models.TextField(max_length=700, help_text='Enter a description about file', null=True, blank=True) + # description = models.TextField(max_length=700, help_text='Enter a description about file', null=True, blank=True) size = models.DecimalField(max_digits=6, decimal_places=2) # updated_on = models.DateTimeField(auto_now=True)