diff --git a/core/forms/__init__.py b/core/forms/__init__.py index b1e1156f..abff8ed9 100644 --- a/core/forms/__init__.py +++ b/core/forms/__init__.py @@ -10,6 +10,8 @@ from .legal_basis import LegalBasisForm from .publication import PublicationForm, PickPublicationForm from .share import ShareForm +from .use_restriction import UseRestrictionForm +from .access import AccessForm @@ -34,5 +36,6 @@ "PublicationForm", "ShareForm", "PickPublicationForm", - "UseRestrictionForm" + "UseRestrictionForm", + "AccessForm", ] diff --git a/core/forms/access.py b/core/forms/access.py index f43cf8d0..5caed33e 100644 --- a/core/forms/access.py +++ b/core/forms/access.py @@ -1,9 +1,10 @@ from django.forms import ModelForm, DateInput, Textarea +from core.forms.dataset import SkipFieldValidationMixin from core.models import Access -class AccessForm(ModelForm): +class AccessForm(SkipFieldValidationMixin, ModelForm): class Meta: model = Access fields = "__all__" @@ -15,15 +16,17 @@ class Meta: # Textareas "access_notes": Textarea(attrs={"rows": 2, "cols": 40}), } - + heading = "Access" + heading_help = "Please provide a help text for Access form" def __init__(self, *args, **kwargs): dataset = kwargs.pop("dataset", None) super().__init__(*args, **kwargs) # we don't allow editing dataset self.fields.pop("dataset") - self.fields["defined_on_locations"].choices = [ - (d.id, d) for d in dataset.data_locations.all() - ] + if dataset: + self.fields["defined_on_locations"].choices = [ + (d.id, d) for d in dataset.data_locations.all() + ] field_order = [ "contact", diff --git a/core/forms/data_declaration.py b/core/forms/data_declaration.py index 04554f68..52df957a 100644 --- a/core/forms/data_declaration.py +++ b/core/forms/data_declaration.py @@ -3,6 +3,7 @@ from django.shortcuts import get_object_or_404 from django.urls import reverse_lazy +from core.forms.dataset import SkipFieldValidationMixin from core.forms.use_restriction import UseRestrictionForm from core.models import DataDeclaration, Partner, Contract, GDPRRole from core.models.contract import PartnerRole @@ -169,10 +170,12 @@ def after_save(self, data_declaration): data_declaration.save() -class DataDeclarationForm(forms.ModelForm): +class DataDeclarationForm(SkipFieldValidationMixin, forms.ModelForm): class Meta: model = DataDeclaration fields = ['title'] + heading = "Data declaration" + heading_help = "Please provide a help text for the Data declaration form" def __init__(self, *args, **kwargs): self.dataset = kwargs.pop('dataset') diff --git a/core/forms/dataset.py b/core/forms/dataset.py index 1f81e9c5..7e62ef73 100644 --- a/core/forms/dataset.py +++ b/core/forms/dataset.py @@ -1,10 +1,22 @@ from django import forms +from django.core.exceptions import ObjectDoesNotExist from django.shortcuts import get_object_or_404 from django.forms import ValidationError from core.models import Dataset, User, Project from core.models.contract import Contract +class SkipFieldValidationMixin: + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['skip_wizard'] = forms.BooleanField(initial=False, required=False, widget=forms.HiddenInput()) + + def is_valid(self): + if self.data.get(f'{self.prefix}-skip_wizard') == 'True': + return True + return super().is_valid() + + class DatasetForm(forms.ModelForm): class Meta: model = Dataset @@ -13,7 +25,8 @@ class Meta: widgets = { 'comments': forms.Textarea(attrs={'rows': 2, 'cols': 40}), } - + heading = "Dataset" + heading_help = "Please provide a help text for the dataset form" def __init__(self, *args, **kwargs): dataset = None diff --git a/core/forms/legal_basis.py b/core/forms/legal_basis.py index 9b4e8f53..e62e3ca3 100644 --- a/core/forms/legal_basis.py +++ b/core/forms/legal_basis.py @@ -1,20 +1,24 @@ from django.forms import ModelForm +from core.forms.dataset import SkipFieldValidationMixin from core.models import LegalBasis -class LegalBasisForm(ModelForm): +class LegalBasisForm(SkipFieldValidationMixin,ModelForm): class Meta: model = LegalBasis fields = '__all__' exclude = [] + heading = "Data Legal Basis" + heading_help = "Please provide a title for this data declaration" def __init__(self, *args, **kwargs): dataset = kwargs.pop('dataset', None) super().__init__(*args, **kwargs) # we don't allow editing dataset self.fields.pop('dataset') - self.fields['data_declarations'].choices = [(d.id, d.title) for d in dataset.data_declarations.all()] + if dataset: + self.fields['data_declarations'].choices = [(d.id, d.title) for d in dataset.data_declarations.all()] field_order = [ 'data_declarations', diff --git a/core/forms/storage_location.py b/core/forms/storage_location.py index 8e3ae95e..1ccfa986 100644 --- a/core/forms/storage_location.py +++ b/core/forms/storage_location.py @@ -1,19 +1,24 @@ from django import forms + +from core.forms.dataset import SkipFieldValidationMixin from core.models.storage_location import DataLocation -class StorageLocationForm(forms.ModelForm): +class StorageLocationForm(SkipFieldValidationMixin, forms.ModelForm): class Meta: model = DataLocation fields = '__all__' exclude = [] + heading = "Add a new storage location" + heading_help = "Please provide a title for this storage location" def __init__(self, *args, **kwargs): dataset = kwargs.pop('dataset', None) super().__init__(*args, **kwargs) # we don't allow editing dataset self.fields.pop('dataset') - self.fields['data_declarations'].choices = [(d.id, d.title) for d in dataset.data_declarations.all()] + if dataset: + self.fields['data_declarations'].choices = [(d.id, d.title) for d in dataset.data_declarations.all()] field_order = [ 'category', diff --git a/web/static/css/dataset_wizard.css b/web/static/css/dataset_wizard.css new file mode 100644 index 00000000..a8433810 --- /dev/null +++ b/web/static/css/dataset_wizard.css @@ -0,0 +1,105 @@ +.step-wizard-list { + background: #fff; + color: #333; + list-style-type: none; + border-radius: 10px; + display: flex; + padding: 20px 10px; + position: relative; + z-index: 10; +} + +.step-wizard-item { + padding: 0 20px; + flex-basis: 0; + -webkit-box-flex: 1; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; + display: flex; + flex-direction: column; + text-align: center; + min-width: 170px; + position: relative; +} + +.step-wizard-item + .step-wizard-item:after { + content: ""; + position: absolute; + left: 0; + top: 19px; + background: #023452; + width: 100%; + height: 2px; + transform: translateX(-50%); + z-index: -10; +} + +.progress-count { + height: 40px; + width: 40px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + font-weight: 600; + margin: 0 auto; + position: relative; + z-index: 10; + color: transparent; +} + +.progress-count:after { + content: ""; + height: 40px; + width: 40px; + background: #023452; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + border-radius: 50%; + z-index: -10; +} + +.progress-count:before { + content: ""; + height: 10px; + width: 20px; + border-left: 3px solid #fff; + border-bottom: 3px solid #fff; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -60%) rotate(-45deg); + transform-origin: center center; +} + +.progress-label { + font-size: 14px; + font-weight: 600; + margin-top: 10px; +} + +.current-item .progress-count:before, +.current-item ~ .step-wizard-item .progress-count:before { + display: none; +} + +.current-item ~ .step-wizard-item .progress-count:after { + height: 10px; + width: 10px; +} + +.current-item ~ .step-wizard-item .progress-label { + opacity: 0.5; +} + +.current-item .progress-count:after { + background: #fff; + border: 2px solid #023452; +} + +.current-item .progress-count { + color: #023452; +} \ No newline at end of file diff --git a/web/static/js/data_declaration.js b/web/static/js/data_declaration.js new file mode 100644 index 00000000..80f249bf --- /dev/null +++ b/web/static/js/data_declaration.js @@ -0,0 +1,29 @@ +const skipInput = document.getElementById(skipInputID) +const skipButton = document.getElementById('skipButton') +if (!skipInput) { + skipButton.remove(); +} +if (skipButton) { + skipButton.addEventListener('click', function () { + if (skipInput) { + skipInput.value = 'True'; + document.getElementById('wizard-form').submit(); + } + }); +} +$(document).ready(function () { + $('input[type=radio]').change(function () { + const submit_button = $("#buttons") + $("#sub-form").remove(); + const sub_form = $("