diff --git a/maps/forms.py b/maps/forms.py index cc6b5e7..fa9e005 100644 --- a/maps/forms.py +++ b/maps/forms.py @@ -7,7 +7,8 @@ from django.template.defaultfilters import safe from django_countries.fields import CountryField from accounts.models import Role, SocialNetwork, UserSocialNetwork -from mdi.models import Organization, Category, Language, OrganizationSocialNetwork, Stage, Type +from mdi.models import Organization, Category, Language, OrganizationSocialNetwork, Stage, Tool, Type, Pricing, License + class BaseForm(forms.Form): error_css_class = 'error' @@ -28,12 +29,14 @@ def __init__(self, *args, **kwargs): super(BaseModelForm, self).__init__(*args, **kwargs) + class IndividualProfileDeleteForm(BaseModelForm): class Meta: model = get_user_model() fields = ['has_profile'] widgets = {'has_profile': HiddenInput} + class IndividualBasicInfoForm(BaseModelForm): first_name = CharField( required=True, @@ -64,6 +67,7 @@ class Meta: 'url': _('Website address') } + class IndividualContactInfoForm(BaseModelForm): city = CharField( required=True, @@ -103,6 +107,7 @@ def __init__(self, *args, **kwargs): self.fields['lat'].value = self.lat self.fields['lng'].value = self.lng + class IndividualRolesForm(BaseForm): roles = forms.ModelMultipleChoiceField( queryset=Role.objects.all(), @@ -158,6 +163,8 @@ class Meta: help_texts = { 'community_skills': _('Provide a short description.') } + + class IndividualDetailedInfoForm(BaseModelForm): class Meta: model = get_user_model() @@ -174,6 +181,7 @@ class Meta: 'projects': _('List any current or past projects you would like to share with others.') } + class IndividualSocialNetworkForm(BaseModelForm): class Meta: model = UserSocialNetwork @@ -188,6 +196,7 @@ class Meta: IndividualSocialNetworkFormSet = formset_factory(IndividualSocialNetworkForm, extra=0) + class OrganizationTypeForm(BaseForm): type = forms.ModelChoiceField( Type.objects.filter(name__in=[ @@ -202,6 +211,8 @@ class OrganizationTypeForm(BaseForm): initial=0, widget=RadioSelect(attrs={'class': 'input-group radio'}) ) + + class OrganizationBasicInfoForm(BaseModelForm): languages = forms.ModelMultipleChoiceField( queryset=Language.objects.all(), @@ -307,6 +318,7 @@ def __init__(self, *args, **kwargs): else: self.fields['year_founded'].required = False + class OrganizationContactInfoForm(BaseModelForm): city = CharField( required=True, @@ -336,6 +348,7 @@ class Meta: 'postal_code': _('ZIP or postal code') } + class OrganizationDetailedInfoForm(BaseModelForm): categories = forms.ModelMultipleChoiceField( queryset=Category.objects.all(), @@ -402,6 +415,7 @@ class Meta: 'sectors': _('Hold down the ctrl (Windows) or command (macOS) key to select multiple options.'), } + class OrganizationScopeAndImpactForm(BaseModelForm): geo_scope = forms.ChoiceField( choices=[ @@ -432,6 +446,7 @@ class Meta: 'impacted_exact_number': _('Include clients and users as well as their family members or others indirectly impacted by the work of your co-operative.') } + class OrganizationSocialNetworkForm(BaseModelForm): class Meta: model = OrganizationSocialNetwork @@ -445,3 +460,70 @@ class Meta: OrganizationSocialNetworkFormSet = formset_factory(OrganizationSocialNetworkForm, extra=0) + + +class ToolBasicInfoForm(BaseModelForm): + class Meta: + model = Tool + fields = [ + 'name', + 'niches', + 'description', + 'url' + ] + labels = { + 'name': _('Name of tool'), + 'niches': _('What is this tool used for?'), + 'description': _('Description of tool'), + 'url': _('URL of tool website') + } + help_texts = { + 'description': _('Max 270 characters.') + } + + +class ToolDetailedInfoForm(BaseModelForm): + pricing = forms.ModelChoiceField( + queryset=Pricing.objects.all(), + empty_label=_('Not sure'), + required=False, + label=_('How much does this tool cost?'), + widget=RadioSelect(attrs={'class': 'input-group radio'}) + ) + + license = forms.ModelChoiceField( + queryset=License.objects.all(), + empty_label=_('Not sure'), + required=False, + label=_('Please choose a specific free / libre / open source license') + ) + + sector = forms.ChoiceField( + choices=[('no', _('No')), ('yes', _('Yes'))], + required=True, + initial='no', + label=_('Is this tool for a specific sector or sectors?'), + widget=RadioSelect(attrs={'class': 'input-group radio'}) + ) + + class Meta: + model = Tool + fields = [ + 'pricing', + 'license_type', + 'license', + 'sector', + 'sectors', + 'languages_supported', + 'coop_made' + ] + labels = { + 'license_type': _('How is this tool licensed?'), + 'sectors': _('Please choose a sector or sectors:'), + 'languages_supported': _('What languages does this tool support?'), + 'coop_made': _('Was this tool created by a co-op?') + } + widgets = { + 'license_type': RadioSelect(attrs={'class': 'input-group radio'}), + 'coop_made': RadioSelect(attrs={'class': 'input-group radio'}) + } diff --git a/maps/static/maps/css/app.scss b/maps/static/maps/css/app.scss index 08daaac..f1dae79 100644 --- a/maps/static/maps/css/app.scss +++ b/maps/static/maps/css/app.scss @@ -136,6 +136,7 @@ select.multiple { textarea + label, select + label, .helptext + label, + .input-group + label, ul.checkbox + label, ul.radio + label { margin-top: rem(45); @@ -519,3 +520,21 @@ h1 + .profile__meta { } } } + +.niches > li { + padding: rem(8) 0 rem(6); + position: relative; +} + +.niches button { + position: absolute; + right: 0; + top: 0; +} + +[for="id_detailed_info-license"], +#id_detailed_info-license, +[for="id_detailed_info-sectors"], +#id_detailed_info-sectors { + display: none; +} diff --git a/maps/static/maps/js/app.js b/maps/static/maps/js/app.js index f51f14e..e83f03e 100644 --- a/maps/static/maps/js/app.js +++ b/maps/static/maps/js/app.js @@ -208,6 +208,55 @@ if (basicInfo) { new Pinecone.Card( card ); }); +[...document.querySelectorAll( '.input-group__parent > li' )].forEach( (container) => { + const input = container.querySelector( '.input--parent' ); + const subInputs = container.querySelectorAll( '.input-group__descendant input' ); + if ( 0 < subInputs.length ) { + new Pinecone.NestedCheckbox( container, input, subInputs ); + } +} ); + +[...document.querySelectorAll( '.filter-disclosure-label' )].forEach( (label) => { + new Pinecone.DisclosureButton( label, { buttonVariant: 'button--disc', visuallyHiddenLabel: true } ); +} ); + +[...document.querySelectorAll('[name="detailed_info-license_type"')].forEach(element => { + element.addEventListener('change', () => { + const license = document.getElementById('id_detailed_info-license'); + const licenseLabel = document.querySelector('[for="id_detailed_info-license"]'); + if (element.value === 'floss' || element.value === 'proprietary-with-floss-integration-tools') { + license.style.display = 'block'; + licenseLabel.style.display = 'block'; + } else { + license.style.display = 'none'; + licenseLabel.style.display = 'none'; + } + }); +}); + +[...document.querySelectorAll('[name="detailed_info-sector"')].forEach(element => { + element.addEventListener('change', () => { + const sectors = document.getElementById('id_detailed_info-sectors'); + const sectorsLabel = document.querySelector('[for="id_detailed_info-sectors"]'); + if (element.value === 'yes') { + sectors.style.display = 'block'; + sectorsLabel.style.display = 'block'; + } else { + sectors.style.display = 'none'; + sectorsLabel.style.display = 'none'; + } + }); +}); + +[...document.querySelectorAll('[role="checkbox"]')].forEach(checkbox => { + checkbox.addEventListener('click', () => { + if (checkbox.getAttribute('aria-checked') !== 'true') { + const disclosureButton = checkbox.parentNode.querySelector('button'); + disclosureButton.setAttribute('aria-expanded', 'true'); + } + }); +}); + [...document.querySelectorAll('.delete-organization')].forEach((form) => { form.addEventListener('submit', (event) => { event.preventDefault(); diff --git a/maps/templates/maps/profiles/tool/basic_info.html b/maps/templates/maps/profiles/tool/basic_info.html new file mode 100644 index 0000000..ccb7c3e --- /dev/null +++ b/maps/templates/maps/profiles/tool/basic_info.html @@ -0,0 +1,66 @@ +{% extends "maps/base.html" %} +{% load i18n %} +{% load maps_extras %} +{% block bodyclass %}form-wizard{% endblock %} +{% block title %}{{ 'Basic information'|titlify }}{% endblock %}} + +{% block content %} +
+ {% include 'maps/profiles/cancel.html' %} +{% endblock %} diff --git a/maps/templates/maps/profiles/tool/detailed_info.html b/maps/templates/maps/profiles/tool/detailed_info.html new file mode 100644 index 0000000..de9c647 --- /dev/null +++ b/maps/templates/maps/profiles/tool/detailed_info.html @@ -0,0 +1,33 @@ +{% extends "maps/base.html" %} +{% load i18n %} +{% load maps_extras %} +{% block bodyclass %}form-wizard{% endblock %} +{% block title %}{{ 'Detailed information'|titlify }}{% endblock %}} + +{% block content %} + + {% include 'maps/profiles/cancel.html' %} +{% endblock %} diff --git a/maps/templatetags/maps_extras.py b/maps/templatetags/maps_extras.py index 6362ae1..cfe70b9 100644 --- a/maps/templatetags/maps_extras.py +++ b/maps/templatetags/maps_extras.py @@ -2,12 +2,14 @@ register = template.Library() + @register.filter(name='titlify') def titlify(value): """Prepends value and emdash to base title""" title_base = 'Platform Co-op Directory' return value + ' – ' + title_base + @register.inclusion_tag('maps/partials/icon.html') def icon(name, **kwargs): modifier = kwargs.get('modifier', name) diff --git a/maps/urls.py b/maps/urls.py index 42488ec..6217a1d 100644 --- a/maps/urls.py +++ b/maps/urls.py @@ -1,15 +1,16 @@ from django.urls import path from django.conf.urls import url from . import views -from .views import INDIVIDUAL_FORMS, ORGANIZATION_FORMS, show_more_about_you_condition, show_scope_and_impact_condition,\ - IndividualProfileWizard, OrganizationProfileWizard, OrganizationDelete, PrivacyPolicyView, TermsOfServiceView, AboutPageView +from .views import INDIVIDUAL_FORMS, ORGANIZATION_FORMS, TOOL_FORMS, show_more_about_you_condition, show_scope_and_impact_condition,\ + IndividualProfileWizard, OrganizationProfileWizard, ToolWizard, OrganizationDelete, PrivacyPolicyView, TermsOfServiceView, AboutPageView from accounts.models import UserSocialNetwork from mdi.models import SocialNetwork urlpatterns = [ path('', views.index, name='index'), - path('profiles/individual', IndividualProfileWizard.as_view(INDIVIDUAL_FORMS, condition_dict={'more_about_you': show_more_about_you_condition}, instance_dict={'social_networks': UserSocialNetwork}), name='individual-profile'), - path('profiles/organization', OrganizationProfileWizard.as_view(ORGANIZATION_FORMS, condition_dict={'scope_and_impact': show_scope_and_impact_condition}, instance_dict={'social_networks': UserSocialNetwork}), name='organization-profile'), + path('add/individual', IndividualProfileWizard.as_view(INDIVIDUAL_FORMS, condition_dict={'more_about_you': show_more_about_you_condition}, instance_dict={'social_networks': UserSocialNetwork}), name='individual-profile'), + path('add/organization', OrganizationProfileWizard.as_view(ORGANIZATION_FORMS, condition_dict={'scope_and_impact': show_scope_and_impact_condition}, instance_dict={'social_networks': UserSocialNetwork}), name='organization-profile'), + path('add/tool', ToolWizard.as_view(TOOL_FORMS), name='add-tool'), path('organizations/