From a5ce725b3006fc34b1b971fa1c508a8ef39e60c5 Mon Sep 17 00:00:00 2001 From: bobbyvish Date: Tue, 2 Jul 2024 15:37:50 +0530 Subject: [PATCH] feat(module 1):multiple image input, form validation --- accounts/api/views.py | 10 + accounts/urls.py | 1 - accounts/views.py | 29 +- manage_events/api/serializers.py | 5 + manage_events/forms.py | 98 ++--- manage_events/urls.py | 5 + manage_events/views.py | 55 ++- templates/accounts/customer/customer_add.html | 46 +-- .../accounts/customer/customer_detail.html | 52 +-- .../accounts/customer/customer_edit.html | 69 ++-- .../accounts/customer/customer_list.html | 16 +- .../cdn_through_html/sweetalert2_cdn_css.html | 3 + .../cdn_through_html/sweetalert2_cdn_js.html | 3 + templates/manage_events/event_add.html | 376 ++++++++++++++---- templates/manage_venues/venue_add.html | 147 ++++--- 15 files changed, 623 insertions(+), 292 deletions(-) create mode 100644 templates/cdn_through_html/sweetalert2_cdn_css.html create mode 100644 templates/cdn_through_html/sweetalert2_cdn_js.html diff --git a/accounts/api/views.py b/accounts/api/views.py index 8aac098..b8ebb56 100644 --- a/accounts/api/views.py +++ b/accounts/api/views.py @@ -1000,6 +1000,16 @@ class AccountTransferCheckView(APIView): def post(self, request, *args, **kwargs): try: obj = IAmPrincipalExtendedData.objects.get(principal=request.user) + except IAmPrincipalExtendedData.DoesNotExist: + # Create a dummy serializer record + obj = { + 'principal': request.user.id, + 'is_onboarded': False, + 'is_transferred': False, + 'transferred_on': None, + 'pwd_changed_post_transfer': False + } + return ApiResponse.success(message=constants.SUCCESS, data=obj) except Exception as e: error_response = { "status": status.HTTP_404_NOT_FOUND, diff --git a/accounts/urls.py b/accounts/urls.py index c51db74..f8f0d1b 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -51,7 +51,6 @@ urlpatterns = [ path('customer/import-customer-data/', views.CustomerImportView.as_view(), name='import_customer_data'), path('customer/export-customer-data/', views.CustomerExportView.as_view(), name='export_customer_data'), - # ignore this to path this for example setup path('principal/example/', TemplateView.as_view(template_name="accounts/iam_module/example_form.html"), name="example_add"), path('datatable/', views.DatatableListView.as_view(), name="serverside_list"), diff --git a/accounts/views.py b/accounts/views.py index 9e450c0..cce5f54 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -754,7 +754,7 @@ class CustomerDetailView(LoginRequiredMixin, generic.DetailView): def get(self, request, *args, **kwargs): principal_obj = IAmPrincipal.objects.get(pk=kwargs.get("pk")) principal_preference = PrincipalPreference.objects.get(principal_id=principal_obj.id) - principal_subscription = PrincipalSubscription.objects.filter(principal=principal_obj).latest("start_date") + principal_subscription = PrincipalSubscription.objects.filter(principal=principal_obj).order_by("-start_date").first() return render(request, self.template_name, locals()) class CustomerListView(LoginRequiredMixin, generic.ListView): @@ -947,7 +947,7 @@ class CustomerImportView(LoginRequiredMixin, generic.View): form = self.form_class() context = self.get_context_data(form=form) return render(request, self.template_name, context=context) - + def post(self, request, *args, **kwargs): form = self.form_class(request.POST, request.FILES) context = self.get_context_data(form=form) @@ -996,11 +996,11 @@ class CustomerImportView(LoginRequiredMixin, generic.View): # collect the principals principal = IAmPrincipal( - first_name=first_name, - last_name=last_name, - email=email, + first_name=first_name.strip().capitalize(), + last_name=last_name.strip().capitalize(), + email=email.strip(), password=make_password("goodtimes#2024"), - username=email, + username=email.strip(), email_verified=True, register_complete=True, principal_type=principal_type @@ -1014,11 +1014,12 @@ class CustomerImportView(LoginRequiredMixin, generic.View): context = self.get_context_data(form=form, error_log=error_log) messages.error(request, "No recore is created check error log and fix the error in the file ") return render(request, self.template_name, context=context) - + # Use transaction.atomic to ensure all-or-nothing with transaction.atomic(): # Bulk create principals - IAmPrincipal.objects.bulk_create(principals) + for principal in principals: + principal.save() # Now we need to refresh principals from the DB to get their IDs principals = IAmPrincipal.objects.filter(email__in=[p.email for p in principals]) @@ -1054,7 +1055,7 @@ class CustomerImportView(LoginRequiredMixin, generic.View): messages.success(request, "Data imported successfully") return render(request, self.template_name, context=context) - + class CustomerExportView(LoginRequiredMixin, generic.View): model = IAmPrincipal @@ -1069,16 +1070,16 @@ class CustomerExportView(LoginRequiredMixin, generic.View): principal.email, principal.first_name, principal.last_name, - str(principal.phone_no), + str(principal.phone_no) if principal.phone_no else "N/A", principal.email_verified, principal.is_active, - principal.extended_data.is_onboarded if principal.extended_data else 'N/A', - principal.extended_data.is_transferred if principal.extended_data else 'N/A', - principal.created_on.replace(tzinfo=None) if principal.created_on else 'N/A' + # principal.extended_data.is_onboarded if principal.extended_data else 'N/A', + # principal.extended_data.is_transferred if principal.extended_data else 'N/A', + # principal.created_on.replace(tzinfo=None) if principal.created_on else 'N/A' ]) # Define the columns for the Excel file - columns = ["Email", "First Name", "Last Name", "Phone Number", "Email Verified", "Active", "Onboarde by Admin", "Transferred to Customer", "Created Date"] + columns = ["Email", "First Name", "Last Name", "Phone Number", "Email Verified", "Active"] # Create a workbook and select the active worksheet wb = Workbook() diff --git a/manage_events/api/serializers.py b/manage_events/api/serializers.py index 5bb1756..17daa07 100644 --- a/manage_events/api/serializers.py +++ b/manage_events/api/serializers.py @@ -38,6 +38,11 @@ class VenueSerializer(serializers.ModelSerializer): fields = "__all__" read_only_fields = ("created_by",) +class VenueShortSerializer(serializers.ModelSerializer): + class Meta: + model = Venue + fields= ["id","title"] + class EventCategorySerializer(serializers.ModelSerializer): class Meta: diff --git a/manage_events/forms.py b/manage_events/forms.py index 71c80a9..9b68fb5 100644 --- a/manage_events/forms.py +++ b/manage_events/forms.py @@ -1,6 +1,6 @@ from django import forms from accounts.models import IAmPrincipal, IAmPrincipalExtendedData -from manage_events.models import EventMaster, Event, EventCategory, Venue +from manage_events.models import EventImage, EventMaster, Event, EventCategory, Venue from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ @@ -17,6 +17,12 @@ class EventCategoryForm(forms.ModelForm): class EventForm(forms.ModelForm): + AGE_GROUP_CHOICES = [ + ('18 & Under', '18 & Under'), + ('18-30', '18-30'), + ('30+', '30+'), + ('Family Event', 'Family Event'), + ] principal = forms.ModelChoiceField( queryset=IAmPrincipal.objects.select_related("extended_data").filter( extended_data__is_onboarded=True, @@ -26,27 +32,30 @@ class EventForm(forms.ModelForm): required=True ) venue = forms.ModelChoiceField( - queryset=Venue.objects.filter( - active=True - ), + queryset=Venue.objects.none(), label="venue", required=True ) + image = forms.ImageField(label="Thumbnail") + event_images = forms.ImageField(label="Event Images") + age_group = forms.ChoiceField(choices=AGE_GROUP_CHOICES, label="Age Group", required=True) + class Meta: model = Event fields = [ "principal", + "venue", "title", # "event_master", "description", "image", - "status", + "event_images", + # "status", "start_date", "end_date", "from_time", "to_time", "category", - "venue", "venue_capacity", # "video_url", "entry_type", @@ -61,57 +70,61 @@ class EventForm(forms.ModelForm): "deleted", ] widgets = { - "title": forms.TextInput(attrs={"class": "form-control"}), - "description": forms.Textarea(attrs={"class": "form-control", "rows": 4}), - "status": forms.Select(attrs={"class": "form-control"}), + "description": forms.Textarea(attrs={"rows": 4}), "start_date": forms.DateInput( - attrs={"class": "form-control", "type": "date"} + attrs={"type": "date"} ), "end_date": forms.DateInput( - attrs={"class": "form-control", "type": "date"} + attrs={"type": "date"} ), "from_time": forms.TimeInput( - attrs={"class": "form-control", "type": "time"} + attrs={"type": "time"} ), - "to_time": forms.TimeInput(attrs={"class": "form-control", "type": "time"}), - "venue_capacity": forms.NumberInput(attrs={"class": "form-control"}), - "video_url": forms.URLInput(attrs={"class": "form-control"}), - "entry_type": forms.Select(attrs={"class": "form-control"}), - "entry_fee": forms.NumberInput(attrs={"class": "form-control"}), - "key_guest": forms.Textarea(attrs={"class": "form-control", "rows": 3}), - "age_group": forms.TextInput(attrs={"class": "form-control"}), - "draft": forms.CheckboxInput(attrs={"class": "form-check-input"}), - # For the 'image' field, you might not need to specify a widget since the default is appropriate. - # However, if you want to add specific classes or attributes, you can do it like this: - "image": forms.FileInput(attrs={"class": "form-control-file"}), - # For ForeignKey fields like 'EventMaster' and 'venue', Django uses a select widget by default. - # You can customize it further if needed: - # "event_master": forms.Select(attrs={"class": "form-control"}), - "venue": forms.Select(attrs={"class": "form-control"}), - "category": forms.Select(attrs={"class": "form-control"}), + "to_time": forms.TimeInput(attrs={"type": "time"}), + "key_guest": forms.Textarea(attrs={"rows": 3}), + "coupon_description": forms.Textarea(attrs={"rows": 3}), } + def __init__(self, *args, **kwargs): + + instance = kwargs.get('instance') + principal_id = kwargs.pop('principal_id', None) + super().__init__(*args, **kwargs) + + if instance: + event_images = EventImage.objects.filter(event=instance) + if event_images.exists(): + self.fields['event_images'].initial = [image.image.url for image in event_images] + + if principal_id: + self.fields['venue'].queryset = Venue.objects.filter(principal_id=principal_id, active=True) + else: + self.fields['venue'].queryset = Venue.objects.none() + def clean(self): cleaned_data = super().clean() # Get the start and end dates from cleaned_data start_date = cleaned_data.get("start_date") end_date = cleaned_data.get("end_date") - - # Validation 1: end_date should not be less than start_date - if end_date and start_date and end_date < start_date: - self.add_error("end_date", _("End date cannot be before the start date.")) - # Get the from and to times from cleaned_data from_time = cleaned_data.get("from_time") to_time = cleaned_data.get("to_time") - # Validation 2: to_time should not be less than or equal to from_time - if to_time and from_time and to_time <= from_time: - self.add_error("to_time", _("End time must be after the start time.")) + # Validation 1: end_date should not be less than start_date + if start_date and end_date and end_date < start_date: + self.add_error("end_date", _("End date cannot be before the start date.")) + + if end_date == start_date: + if to_time and from_time and to_time <= from_time: + self.add_error("to_time", _("End time must be after the start time.")) return cleaned_data +class EventImageForm(forms.ModelForm): + class Meta: + model = EventImage + fields = ['image'] class EventMasterForm(forms.ModelForm): class Meta: @@ -134,19 +147,14 @@ class VenueForm(forms.ModelForm): fields = [ "principal", "title", - "description", + # "description", "address", "image", - "url", + # "url", "latitude", "longitude", ] widgets = { - "title": forms.TextInput(attrs={"class": "form-control"}), - "description": forms.Textarea(attrs={"class": "form-control", "rows": 4}), - "address": forms.Textarea(attrs={"class": "form-control", "rows": 3}), - "image": forms.FileInput(attrs={"class": "form-control"}), - "url": forms.URLInput(attrs={"class": "form-control"}), - "latitude": forms.NumberInput(attrs={"class": "form-control"}), - "longitude": forms.NumberInput(attrs={"class": "form-control"}), + "description": forms.Textarea(attrs={"rows": 4}), + "address": forms.Textarea(attrs={"rows": 3}), } diff --git a/manage_events/urls.py b/manage_events/urls.py index 6ce1536..bcc3182 100644 --- a/manage_events/urls.py +++ b/manage_events/urls.py @@ -89,6 +89,11 @@ urlpatterns = [ views.VenueDeleteView.as_view(), name="venue_delete", ), + path( + "venue/customer/", + views.CustomerVenueFilterView.as_view(), + name="venue_customer_filter", + ), path( "generate-event-report//", views.GenerateEventReportView.as_view(), diff --git a/manage_events/views.py b/manage_events/views.py index 56b42a5..85e9f28 100644 --- a/manage_events/views.py +++ b/manage_events/views.py @@ -1,5 +1,7 @@ from django.shortcuts import get_object_or_404, redirect, render from accounts import resource_action +from goodtimes.utils import JsonResponseUtil +from manage_events.api.serializers import VenueSerializer, VenueShortSerializer from manage_events.forms import ( EventMasterForm, EventCategoryForm, @@ -7,7 +9,7 @@ from manage_events.forms import ( VenueForm, ) from django.core.paginator import Paginator -from .models import EventMaster, Event, EventCategory, EventPrincipalInteraction, Venue +from .models import EventImage, EventMaster, Event, EventCategory, EventPrincipalInteraction, Venue from django.views import generic from django.contrib.auth.mixins import LoginRequiredMixin from django.urls import reverse_lazy @@ -222,7 +224,7 @@ class EventMasterDeleteView(LoginRequiredMixin, generic.View): return redirect(self.success_url) - +from django.core.files.storage import default_storage class EventCreateOrUpdateView(LoginRequiredMixin, generic.View): # Set the page_name and resource page_name = resource_action.RESOURCE_MANAGE_EVENTS @@ -257,6 +259,9 @@ class EventCreateOrUpdateView(LoginRequiredMixin, generic.View): } context.update(kwargs) # Include any additional context data passed to the view return context + + def get_event_images(self): + return [image.image.url for image in EventImage.objects.filter(event=self.object)] def get(self, request, *args, **kwargs): self.object = self.get_object() @@ -266,25 +271,46 @@ class EventCreateOrUpdateView(LoginRequiredMixin, generic.View): self.action = resource_action.ACTION_UPDATE form = self.form_class(instance=self.object) - context = self.get_context_data(form=form) + + context = self.get_context_data(form=form, event_images_urls=self.get_event_images()) return render(request, self.template_name, context=context) def post(self, request, *args, **kwargs): + print(request.POST) + print(request.FILES) self.object = self.get_object() # If an object is found, change action to ACTION_UPDATE if self.object is not None: self.action = resource_action.ACTION_UPDATE - form = self.form_class(request.POST, request.FILES, instance=self.object) + principal_id = request.POST.get('principal') + + form = self.form_class(request.POST, request.FILES, instance=self.object, principal_id=principal_id) if not form.is_valid(): - print(form.errors) - context = self.get_context_data(form=form) + print(f"form error is {form.errors}") + context = self.get_context_data(form=form, event_images_urls=self.get_event_images()) return render(request, self.template_name, context=context) instance = form.save() instance.created_by = form.cleaned_data.get("principal") instance.save() + + # Delete old images from storage + old_images = EventImage.objects.filter(event=instance) + for old_image in old_images: + if default_storage.exists(old_image.image.name): + default_storage.delete(old_image.image.name) + + # Delete old images from database + old_images.delete() + + event_images = request.FILES.getlist("event_images") + event_image_objects = [EventImage(event=instance, image=image) for image in event_images] + + EventImage.objects.bulk_create(event_image_objects) + messages.success(self.request, self.get_success_message()) + return redirect(self.success_url) @@ -487,6 +513,23 @@ class VenueDeleteView(LoginRequiredMixin, generic.View): return redirect(self.success_url) +class CustomerVenueFilterView(LoginRequiredMixin, generic.View): + model = Venue + serializer_class = VenueShortSerializer + + def get(self, request, *args, **kwargs): + pk = request.GET.get("pk", None) + if not pk: + return JsonResponseUtil.error(message="Non transfer user list field is required") + obj = self.model.objects.filter(principal=pk) + if not obj.exists(): + return JsonResponseUtil.error(message="No venue found for the given user.") + + serializer = self.serializer_class(obj, many=True) + + return JsonResponseUtil.success(message=constants.SUCCESS, data=serializer.data) + + User = get_user_model() from .report import generate_event_report, generate_event_report_pdf_three from django.http import HttpResponse diff --git a/templates/accounts/customer/customer_add.html b/templates/accounts/customer/customer_add.html index 77bd818..723c9fb 100644 --- a/templates/accounts/customer/customer_add.html +++ b/templates/accounts/customer/customer_add.html @@ -57,7 +57,7 @@ // }); var start_date = flatpickr(document.getElementById('id_free_start_date'), { - minDate: "today", + // minDate: "today", onChange: function(selectedDates, dateStr, instance) { end_date.set('minDate', selectedDates[0]); } @@ -113,30 +113,30 @@ email: { required: true, validEmail: true, - remote: { - url: "{% url 'accounts:customer_check_email' %}", // Replace with your actual URL for the view - type: "POST", - data: { - email: function() { - return $("#id_email").val(); - } - }, - beforeSend: function(xhr) { - xhr.setRequestHeader('X-CSRFToken', $('input[name="csrfmiddlewaretoken"]').val()); - }, - success: function(data) { - console.log(date) - // Handle successful email check (optional) - // You can remove this if you only need to display the error message - }, - error: function(response) { - console.log(response) - } - }, + // remote: { + // url: "{% url 'accounts:customer_check_email' %}", // Replace with your actual URL for the view + // type: "POST", + // data: { + // email: function() { + // return $("#id_email").val(); + // } + // }, + // beforeSend: function(xhr) { + // xhr.setRequestHeader('X-CSRFToken', $('input[name="csrfmiddlewaretoken"]').val()); + // }, + // success: function(data) { + // console.log(date) + // // Handle successful email check (optional) + // // You can remove this if you only need to display the error message + // }, + // error: function(response) { + // console.log(response) + // } + // }, }, preferences: { required: true, - minlength: 3 + minlength: 1 }, free_start_date: { required: true, @@ -183,7 +183,7 @@ var startDate = $("#id_free_start_date").datepicker("getDate"); // Assuming you're using datepicker var endDate = $(element).datepicker("getDate"); if (!endDate || !startDate) { - return true; // Allow if either date is not selected (prevents errors) + return true; // Allow if either date is not selected (prevents errors) } return endDate > startDate; } diff --git a/templates/accounts/customer/customer_detail.html b/templates/accounts/customer/customer_detail.html index 9d48e5c..438b117 100644 --- a/templates/accounts/customer/customer_detail.html +++ b/templates/accounts/customer/customer_detail.html @@ -30,28 +30,17 @@
{{principal_obj.first_name}}
- - -
- +
+
Last Name
-
{{principal_obj.last_name}}
- -
- -
- +
+
Email Address
-
{{principal_obj.email}}
- -
- -
- +
+
Preferences
-
{% for category in principal_preference.preferred_categories.all %} @@ -62,31 +51,24 @@ No preferred categories. {% endfor %} +
- - - -
- -
Start Date
- -
{{principal_subscription.start_date}}
- -
-
- +
+
Start Date
+
{% if principal_subscription %}{{ principal_subscription.start_date }}{% else %}No subscription found{% endif %}
+
+
End Date
- -
{{principal_subscription.end_date}}
- -
- {% if not principal_obj.extended_data.is_transferred %} +
{% if principal_subscription %}{{ principal_subscription.end_date }}{% else %}No subscription found{% endif %}
+
+ {% if principal_obj.extended_data and not principal_obj.extended_data.transferred and principal_obj.extended_data.onboarded and principal_obj.principal_type.name == 'event_manager' %} {% endif %} + diff --git a/templates/accounts/customer/customer_edit.html b/templates/accounts/customer/customer_edit.html index 8cebe76..d5b3009 100644 --- a/templates/accounts/customer/customer_edit.html +++ b/templates/accounts/customer/customer_edit.html @@ -19,13 +19,14 @@ - {% if not principal_obj.extended_data.is_transferred %} + {% if principal_obj.extended_data and not principal_obj.extended_data.transferred and principal_obj.extended_data.onboarded and principal_obj.principal_type.name == 'event_manager' %} {% endif %} +
@@ -65,7 +66,7 @@ // }); var start_date = flatpickr(document.getElementById('id_free_start_date'), { - minDate: "today", + // minDate: "today", onChange: function(selectedDates, dateStr, instance) { end_date.set('minDate', selectedDates[0]); } @@ -121,30 +122,30 @@ email: { required: true, validEmail: true, - remote: { - url: "{% url 'accounts:customer_check_email' %}", // Replace with your actual URL for the view - type: "POST", - data: { - email: function() { - return $("#id_email").val(); - } - }, - beforeSend: function(xhr) { - xhr.setRequestHeader('X-CSRFToken', $('input[name="csrfmiddlewaretoken"]').val()); - }, - success: function(data) { - console.log(date) - // Handle successful email check (optional) - // You can remove this if you only need to display the error message - }, - error: function(response) { - console.log(response) - } - }, + // remote: { + // url: "{% url 'accounts:customer_check_email' %}", // Replace with your actual URL for the view + // type: "POST", + // data: { + // email: function() { + // return $("#id_email").val(); + // } + // }, + // beforeSend: function(xhr) { + // xhr.setRequestHeader('X-CSRFToken', $('input[name="csrfmiddlewaretoken"]').val()); + // }, + // success: function(data) { + // console.log(date) + // // Handle successful email check (optional) + // // You can remove this if you only need to display the error message + // }, + // error: function(response) { + // console.log(response) + // } + // }, }, preferences: { required: true, - minlength: 3 + minlength: 1 }, free_start_date: { required: true, @@ -186,16 +187,16 @@ greaterThanStartDate: "The end date must be after the start date." } }, - customMethods: { - greaterThanStartDate: function(element) { - var startDate = $("#id_free_start_date").datepicker("getDate"); // Assuming you're using datepicker - var endDate = $(element).datepicker("getDate"); - if (!endDate || !startDate) { - return true; // Allow if either date is not selected (prevents errors) - } - return endDate > startDate; - } - }, + // customMethods: { + // greaterThanStartDate: function(element) { + // var startDate = $("#id_free_start_date").datepicker("getDate"); // Assuming you're using datepicker + // var endDate = $(element).datepicker("getDate"); + // if (!endDate || !startDate) { + // return true; // Allow if either date is not selected (prevents errors) + // } + // return endDate > startDate; + // } + // }, errorElement: 'div', errorPlacement: function(error, element) { error.addClass('invalid-feedback'); diff --git a/templates/accounts/customer/customer_list.html b/templates/accounts/customer/customer_list.html index 4056566..ad1825f 100644 --- a/templates/accounts/customer/customer_list.html +++ b/templates/accounts/customer/customer_list.html @@ -98,13 +98,21 @@ {{ data_obj.email_verified }} {{ data_obj.referral_count }} - - {{ data_obj.extended_data.is_onboarded }} + + {% if data_obj.extended_data %} + {{ data_obj.extended_data.is_onboarded }} + {% else %} + False + {% endif %} - - {{ data_obj.extended_data.is_transferred }} + + {% if data_obj.extended_data %} + {{ data_obj.extended_data.is_transferred }} + {% else %} + False + {% endif %} {{ data_obj.created_on }} diff --git a/templates/cdn_through_html/sweetalert2_cdn_css.html b/templates/cdn_through_html/sweetalert2_cdn_css.html new file mode 100644 index 0000000..01bab28 --- /dev/null +++ b/templates/cdn_through_html/sweetalert2_cdn_css.html @@ -0,0 +1,3 @@ +{% load static%} + + diff --git a/templates/cdn_through_html/sweetalert2_cdn_js.html b/templates/cdn_through_html/sweetalert2_cdn_js.html new file mode 100644 index 0000000..00a21b9 --- /dev/null +++ b/templates/cdn_through_html/sweetalert2_cdn_js.html @@ -0,0 +1,3 @@ +{% load static%} + + \ No newline at end of file diff --git a/templates/manage_events/event_add.html b/templates/manage_events/event_add.html index 66abf4b..2a598cb 100644 --- a/templates/manage_events/event_add.html +++ b/templates/manage_events/event_add.html @@ -4,8 +4,10 @@ {% include "cdn_through_html/filepond_cdn_css.html" %} + {% include "cdn_through_html/flatpicker_cdn_css.html" %} {% include "cdn_through_html/quill_cdn_css.html" %} {% include "cdn_through_html/tagify_cdn_css.html" %} + {% include "cdn_through_html/sweetalert2_cdn_css.html" %} {{form.media}} {% endblock %} @@ -28,32 +30,13 @@
-
+ {% csrf_token %} {% include 'includes/dynamic_template_form.html' with form=form %} +
-
+
- {% comment %}
- - -
We'll never share your email with anyone else.
-
-
- -
-
-
- -
-
    -
    -
    -
    - - -
    - {% endcomment %}
    @@ -70,64 +53,305 @@ {% include "cdn_through_html/filepond_cdn_js.html" %} - {% include "cdn_through_html/quill_cdn_js.html" %} + {% include "cdn_through_html/flatpicker_cdn_js.html" %} {% include "cdn_through_html/tagify_cdn_js.html" %} + {% include "cdn_through_html/sweetalert2_cdn_js.html" %} + {% include "cdn_through_html/jquery_validate_cdn_js.html" %} + {% endblock %} \ No newline at end of file diff --git a/templates/manage_venues/venue_add.html b/templates/manage_venues/venue_add.html index c7e1081..deb9b5c 100644 --- a/templates/manage_venues/venue_add.html +++ b/templates/manage_venues/venue_add.html @@ -4,8 +4,6 @@ {% include "cdn_through_html/filepond_cdn_css.html" %} -{% include "cdn_through_html/quill_cdn_css.html" %} -{% include "cdn_through_html/tagify_cdn_css.html" %} {{form.media}} {% endblock %} @@ -28,7 +26,7 @@
    -
    + {% csrf_token %} {% include 'includes/dynamic_template_form.html' with form=form %}
    @@ -56,64 +54,105 @@ {% include "cdn_through_html/filepond_cdn_js.html" %} -{% include "cdn_through_html/quill_cdn_js.html" %} -{% include "cdn_through_html/tagify_cdn_js.html" %} + +{% include "cdn_through_html/jquery_validate_cdn_js.html" %} {% endblock %} \ No newline at end of file