@@ -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,
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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}),
|
||||
}
|
||||
|
||||
@@ -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/<int:user_id>/",
|
||||
views.GenerateEventReportView.as_view(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -30,28 +30,17 @@
|
||||
|
||||
<div class="col-md-9">{{principal_obj.first_name}}</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">Last Name</div>
|
||||
|
||||
<div class="col-md-9">{{principal_obj.last_name}}</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">Email Address</div>
|
||||
|
||||
<div class="col-md-9">{{principal_obj.email}}</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">Preferences</div>
|
||||
|
||||
<div class="col-md-9">
|
||||
{% for category in principal_preference.preferred_categories.all %}
|
||||
<span class="shadow-none badge badge-primary mb-1">
|
||||
@@ -62,31 +51,24 @@
|
||||
No preferred categories.
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
|
||||
<div class="col-md-3">Start Date</div>
|
||||
|
||||
<div class="col-md-9">{{principal_subscription.start_date}}</div>
|
||||
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">Start Date</div>
|
||||
<div class="col-md-9">{% if principal_subscription %}{{ principal_subscription.start_date }}{% else %}No subscription found{% endif %}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">End Date</div>
|
||||
|
||||
<div class="col-md-9">{{principal_subscription.end_date}}</div>
|
||||
|
||||
</div>
|
||||
{% if not principal_obj.extended_data.is_transferred %}
|
||||
<div class="col-md-9">{% if principal_subscription %}{{ principal_subscription.end_date }}{% else %}No subscription found{% endif %}</div>
|
||||
</div>
|
||||
{% 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' %}
|
||||
<div class="col text-end">
|
||||
<a class="btn btn-primary mb-3" href="{% url 'accounts:customer_transfer' principal_obj.id%}">
|
||||
<a class="btn btn-dark mb-2 me-4" href="{% url 'accounts:customer_transfer' principal_obj.id %}">
|
||||
Transfer Account
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,13 +19,14 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% 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' %}
|
||||
<div class="col text-end">
|
||||
<a class="btn btn-dark mb-2 me-4" href="{% url 'accounts:customer_transfer' principal_obj.id%}">
|
||||
<a class="btn btn-dark mb-2 me-4" href="{% url 'accounts:customer_transfer' principal_obj.id %}">
|
||||
Transfer Account
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
<div class="row layout-spacing">
|
||||
<div class="col-lg-12">
|
||||
@@ -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');
|
||||
|
||||
@@ -98,13 +98,21 @@
|
||||
<td>{{ data_obj.email_verified }}</td>
|
||||
<td>{{ data_obj.referral_count }}</td>
|
||||
<td class="text-center">
|
||||
<span class="shadow-none badge {% if data_obj.extended_data.is_onboarded %}badge-primary{% else %}badge-danger{% endif %}">
|
||||
{{ data_obj.extended_data.is_onboarded }}
|
||||
<span class="shadow-none badge {% if data_obj.extended_data and data_obj.extended_data.is_onboarded %}badge-primary{% else %}badge-danger{% endif %}">
|
||||
{% if data_obj.extended_data %}
|
||||
{{ data_obj.extended_data.is_onboarded }}
|
||||
{% else %}
|
||||
False
|
||||
{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span class="shadow-none badge {% if data_obj.extended_data.is_transferred %}badge-primary{% else %}badge-danger{% endif %}">
|
||||
{{ data_obj.extended_data.is_transferred }}
|
||||
<span class="shadow-none badge {% if data_obj.extended_data and data_obj.extended_data.is_transferred %}badge-primary{% else %}badge-danger{% endif %}">
|
||||
{% if data_obj.extended_data %}
|
||||
{{ data_obj.extended_data.is_transferred }}
|
||||
{% else %}
|
||||
False
|
||||
{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ data_obj.created_on }}</td>
|
||||
|
||||
3
templates/cdn_through_html/sweetalert2_cdn_css.html
Normal file
3
templates/cdn_through_html/sweetalert2_cdn_css.html
Normal file
@@ -0,0 +1,3 @@
|
||||
{% load static%}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'src/plugins/src/sweetalerts2/sweetalerts2.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'src/plugins/css/light/sweetalerts2/custom-sweetalert.css' %}">
|
||||
3
templates/cdn_through_html/sweetalert2_cdn_js.html
Normal file
3
templates/cdn_through_html/sweetalert2_cdn_js.html
Normal file
@@ -0,0 +1,3 @@
|
||||
{% load static%}
|
||||
<script src="{% static 'src/plugins/src/sweetalerts2/sweetalerts2.min.js' %}"></script>
|
||||
<script src="{% static 'src/plugins/src/sweetalerts2/custom-sweetalert.js' %}"></script>
|
||||
@@ -4,8 +4,10 @@
|
||||
<!-- include required css cdn link through html here -->
|
||||
|
||||
{% 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 @@
|
||||
<div class="statbox widget box box-shadow">
|
||||
<div class="widget-content widget-content-area">
|
||||
|
||||
<form method="POST" enctype="multipart/form-data" novalidate>
|
||||
<form method="POST" enctype="multipart/form-data" id="eventForm">
|
||||
{% csrf_token %}
|
||||
{% include 'includes/dynamic_template_form.html' with form=form %}
|
||||
|
||||
<div class="mt-4 mb-0">
|
||||
<div class="d-grid"><button class="btn btn-primary btn-block" type="submit">Submit</button></div>
|
||||
<div class="d-grid"><button class="btn btn-primary btn-block">Submit</button></div>
|
||||
</div>
|
||||
{% comment %} <div class="mb-3">
|
||||
<label for="title" class="form-label">Title</label>
|
||||
<input type="text" class="form-control" id="title" aria-describedby="title">
|
||||
<div id="emailHelp" class="form-text" style="color: grey;">We'll never share your email with anyone else.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Description</label>
|
||||
<div id="description"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="product-images">Image</label>
|
||||
<div class="multiple-file-upload">
|
||||
<div class="filepond--root filepond file-upload-multiple filepond--hopper" id="images" data-style-button-remove-item-position="left" data-style-button-process-item-position="right" data-style-load-indicator-position="right" data-style-progress-indicator-position="right" data-style-button-remove-item-align="false" style="height: 57px;"><input class="filepond--browser" type="file" id="filepond--browser-feeq8o6dj" name="filepond" aria-controls="filepond--assistant-feeq8o6dj" aria-labelledby="filepond--drop-label-feeq8o6dj" multiple=""><a class="filepond--credits" aria-hidden="true" href="https://pqina.nl/" target="_blank" rel="noopener noreferrer" style="transform: translateY(49px);">Powered by PQINA</a><div class="filepond--drop-label" style="transform: translate3d(0px, 0px, 0px); opacity: 1;"><label for="filepond--browser-feeq8o6dj" id="filepond--drop-label-feeq8o6dj" aria-hidden="true">Drag & Drop your files or <span class="filepond--label-action" tabindex="0">Browse</span></label></div><div class="filepond--list-scroller" style="transform: translate3d(0px, 41px, 0px);"><ul class="filepond--list" role="list"></ul></div><div class="filepond--panel filepond--panel-root" data-scalable="true"><div class="filepond--panel-top filepond--panel-root"></div><div class="filepond--panel-center filepond--panel-root" style="transform: translate3d(0px, 8px, 0px) scale3d(1, 0.41, 1);"></div><div class="filepond--panel-bottom filepond--panel-root" style="transform: translate3d(0px, 49px, 0px);"></div></div><span class="filepond--assistant" id="filepond--assistant-feeq8o6dj" role="status" aria-live="polite" aria-relevant="additions"></span><div class="filepond--drip"></div><fieldset class="filepond--data"></fieldset></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="tags">Tags</label>
|
||||
<input id="tags" class="tags" value="">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button> {% endcomment %}
|
||||
</form>
|
||||
|
||||
</div>
|
||||
@@ -70,64 +53,305 @@
|
||||
<!-- include required css cdn link through html here -->
|
||||
|
||||
{% 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" %}
|
||||
|
||||
|
||||
<script>
|
||||
/**
|
||||
* ===================================
|
||||
* Blog Description Editor
|
||||
* ===================================
|
||||
*/
|
||||
var quill = new Quill('#description', {
|
||||
modules: {
|
||||
toolbar: [
|
||||
[{ header: [1, 2, false] }],
|
||||
['bold', 'italic', 'underline'],
|
||||
['image', 'code-block']
|
||||
]
|
||||
},
|
||||
placeholder: 'Write description...',
|
||||
theme: 'snow' // or 'bubble'
|
||||
// Function to initialize Quill editor
|
||||
function initializeQuillEditor(selector, placeholder) {
|
||||
return new Quill(selector, {
|
||||
modules: {
|
||||
toolbar: [
|
||||
[{ header: [1, 2, false] }],
|
||||
['bold', 'italic', 'underline'],
|
||||
['image', 'code-block']
|
||||
]
|
||||
},
|
||||
placeholder: placeholder,
|
||||
theme: 'snow' // or 'bubble'
|
||||
});
|
||||
}
|
||||
|
||||
// Function to initialize FilePond
|
||||
function initializeFilePond(selector, allowMultiple = false) {
|
||||
FilePond.registerPlugin(
|
||||
FilePondPluginImagePreview,
|
||||
FilePondPluginImageExifOrientation,
|
||||
FilePondPluginFileValidateSize
|
||||
);
|
||||
return FilePond.create(document.getElementById(selector),{
|
||||
allowMultiple: allowMultiple,
|
||||
storeAsFile: true,
|
||||
dropOnPage: true
|
||||
});
|
||||
}
|
||||
|
||||
// Function to initialize Flatpickr
|
||||
function initializeFlatpickr(selector, config) {
|
||||
return flatpickr(document.getElementById(selector), config);
|
||||
}
|
||||
|
||||
// Function to initialize Tagify
|
||||
function initializeTagify(selector) {
|
||||
var input = document.querySelector(selector);
|
||||
return new Tagify(input, {
|
||||
originalInputValueFormat: valuesArr => valuesArr.map(item => item.value).join(', ')
|
||||
});
|
||||
}
|
||||
|
||||
// Function to handle principal change event and fetch venues
|
||||
function handlePrincipalChange() {
|
||||
$("#id_principal").change(function(){
|
||||
var selectedPrincipalId = $(this).val();
|
||||
|
||||
// clear existing venue options
|
||||
$("#id_venue").empty().append($("<option value='' selected=''>---------</option>"));
|
||||
|
||||
if (selectedPrincipalId){
|
||||
$.ajax({
|
||||
url: "{% url 'manage_events:venue_customer_filter'%}",
|
||||
type: "GET",
|
||||
data: { pk: selectedPrincipalId },
|
||||
success: function(data) {
|
||||
// Process and populate venue options
|
||||
$.each(data.data, function(index, venue) {
|
||||
$("#id_venue").append($("<option></option>").val(venue.id).text(venue.title));
|
||||
});
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
console.error("Error fetching venue data:", textStatus, errorThrown);
|
||||
console.error("Error fetching venue data:", jqXHR.responseJSON.message);
|
||||
// Display error alert using SweetAlert2
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
text: jqXHR.responseJSON.message,
|
||||
position: 'center',
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// Set multiple attribute for event images
|
||||
document.getElementById('id_event_images').setAttribute('multiple', '');
|
||||
|
||||
// Initialize Quill editor
|
||||
var quill = initializeQuillEditor('#description', 'Write description...');
|
||||
|
||||
// Initialize FilePond
|
||||
var thumbnail = initializeFilePond('id_image');
|
||||
var thumbnailUrl = "{% if form.image.value %}{{ form.image.value.url }}{% endif %}";
|
||||
if (thumbnailUrl){
|
||||
thumbnail.addFile(thumbnailUrl)
|
||||
}
|
||||
|
||||
var eventImages = initializeFilePond('id_event_images', true);
|
||||
// var eventImagesUrl = "{% if form.event_images.value %}{{ form.event_images.value.url}}{%endif %}";
|
||||
var eventImagesUrls = {{ event_images_urls|safe }};
|
||||
|
||||
if (Array.isArray(eventImagesUrls) && eventImagesUrls.length) {
|
||||
eventImagesUrls.forEach(function(url) {
|
||||
eventImages.addFile(url);
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize Flatpickr
|
||||
var startDate = initializeFlatpickr('id_start_date', {
|
||||
minDate: "today",
|
||||
onChange: function(selectedDates, dateStr, instance) {
|
||||
endDate.set('minDate', selectedDates[0]);
|
||||
}
|
||||
});
|
||||
|
||||
var endDate = initializeFlatpickr('id_end_date', { minDate: null });
|
||||
|
||||
var fromTimeField = document.getElementById('id_from_time');
|
||||
var fromTimeValue = fromTimeField.value; // Get the current value of the form field
|
||||
|
||||
initializeFlatpickr('id_from_time', {
|
||||
enableTime: true,
|
||||
noCalendar: true,
|
||||
dateFormat: "H:i",
|
||||
defaultDate: fromTimeValue ? fromTimeValue : new Date(), // Use form field value if available, otherwise use current time
|
||||
allowInput: true,
|
||||
});
|
||||
|
||||
var fromTimeField = document.getElementById('id_to_time');
|
||||
var fromTimeValue = fromTimeField.value; // Get the current value of the form field
|
||||
|
||||
initializeFlatpickr('id_to_time', {
|
||||
enableTime: true,
|
||||
noCalendar: true,
|
||||
dateFormat: "H:i",
|
||||
defaultDate: fromTimeValue ? fromTimeValue : new Date(),
|
||||
allowInput: true,
|
||||
});
|
||||
|
||||
// Initialize Tagify
|
||||
initializeTagify('#id_tags');
|
||||
|
||||
// Handle principal change
|
||||
handlePrincipalChange();
|
||||
|
||||
// Initialize jQuery Validate
|
||||
$("#eventForm").validate({
|
||||
rules: {
|
||||
principal: {
|
||||
required: true
|
||||
},
|
||||
venue: {
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
required: true
|
||||
},
|
||||
description: {
|
||||
required: true
|
||||
},
|
||||
image: {
|
||||
required: true,
|
||||
accept: "image/*"
|
||||
},
|
||||
event_images: {
|
||||
required: true,
|
||||
accept: "image/*"
|
||||
},
|
||||
start_date: {
|
||||
required: true,
|
||||
date: true
|
||||
},
|
||||
end_date: {
|
||||
required: true,
|
||||
date: true
|
||||
},
|
||||
from_time: {
|
||||
required: true
|
||||
},
|
||||
to_time: {
|
||||
required: true
|
||||
},
|
||||
category:{
|
||||
required: true
|
||||
},
|
||||
venue_capacity:{
|
||||
required: true
|
||||
},
|
||||
entry_type: {
|
||||
required: true
|
||||
},
|
||||
age_group: {
|
||||
required: true
|
||||
},
|
||||
tags: {
|
||||
required: true
|
||||
}
|
||||
// Add other fields with similar structure
|
||||
},
|
||||
messages: {
|
||||
principal: {
|
||||
required: "Please select a principal"
|
||||
},
|
||||
venue: {
|
||||
required: "Please select a venue"
|
||||
},
|
||||
title: {
|
||||
required: "Please enter a title",
|
||||
},
|
||||
description: {
|
||||
required: "Please enter a description",
|
||||
},
|
||||
image: {
|
||||
required: "Please upload a thumbnail",
|
||||
accept: "Please upload a valid image file"
|
||||
},
|
||||
event_images: {
|
||||
required: "Please upload event images",
|
||||
accept: "Please upload valid image files"
|
||||
},
|
||||
start_date: {
|
||||
required: "Please select a start date",
|
||||
date: "Please enter a valid date"
|
||||
},
|
||||
end_date: {
|
||||
required: "Please select an end date",
|
||||
date: "Please enter a valid date",
|
||||
greaterThanStartDate: "The end date must be after or equal to the start date."
|
||||
},
|
||||
from_time: {
|
||||
required: "Please select a start time"
|
||||
},
|
||||
to_time: {
|
||||
required: "Please select an end time",
|
||||
greaterThanFromTime: "End time must be greater than start time on the same day"
|
||||
},
|
||||
category:{
|
||||
required: "Please select a category"
|
||||
},
|
||||
venue_capacity:{
|
||||
required: "Please enter a venue capacity"
|
||||
},
|
||||
entry_type: {
|
||||
required: "Please select a event type"
|
||||
},
|
||||
age_group: {
|
||||
required: "Please select a age group"
|
||||
},
|
||||
tags: {
|
||||
required: "Please enter tags"
|
||||
}
|
||||
},
|
||||
customMethods: {
|
||||
greaterThanStartDate: function(element) {
|
||||
var startDate = $("#id_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;
|
||||
},
|
||||
greaterThanFromTime: function(element){
|
||||
var startDateVal = $("#id_start_date").val();
|
||||
var endDateVal = $("#id_end_date").val();
|
||||
var fromTime = $("#id_from_time").val();
|
||||
var toTime = $(element).val();
|
||||
if (!toTime || !fromTime) {
|
||||
return true; // Allow if either time is not selected (prevents errors)
|
||||
}
|
||||
if (startDateVal !== endDateVal) {
|
||||
return true
|
||||
}
|
||||
return toTime > fromTime;
|
||||
}
|
||||
},
|
||||
errorElement: "div",
|
||||
errorPlacement: function(error, element) {
|
||||
error.addClass("invalid-feedback");
|
||||
if (element.prop("type") === "checkbox") {
|
||||
error.insertAfter(element.next("label"));
|
||||
} else {
|
||||
error.insertAfter(element);
|
||||
}
|
||||
},
|
||||
highlight: function(element) {
|
||||
$(element).addClass("is-invalid").removeClass("is-valid");
|
||||
},
|
||||
unhighlight: function(element) {
|
||||
$(element).addClass("is-valid").removeClass("is-invalid");
|
||||
},
|
||||
submitHandler: function(form) {
|
||||
|
||||
// Disable the submit button to prevent multiple submissions
|
||||
$('button[type="submit"]').prop('disabled', true);
|
||||
event.prevent()
|
||||
//form.submit();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* ====================
|
||||
* File Pond
|
||||
* ====================
|
||||
*/
|
||||
|
||||
// We want to preview images, so we register
|
||||
// the Image Preview plugin, We also register
|
||||
// exif orientation (to correct mobile image
|
||||
// orientation) and size validation, to prevent
|
||||
// large files from being added
|
||||
FilePond.registerPlugin(
|
||||
FilePondPluginImagePreview,
|
||||
FilePondPluginImageExifOrientation,
|
||||
FilePondPluginFileValidateSize,
|
||||
// FilePondPluginImageEdit
|
||||
);
|
||||
|
||||
// Select the file input and use
|
||||
// create() to turn it into a pond
|
||||
var ecommerce = FilePond.create(document.querySelector('.file-upload-multiple'));
|
||||
|
||||
|
||||
/**
|
||||
* =====================
|
||||
* Blog Tags
|
||||
* =====================
|
||||
*/
|
||||
// The DOM element you wish to replace with Tagify
|
||||
var input = document.querySelector('#id_tags');
|
||||
|
||||
// initialize Tagify on the above input node reference
|
||||
new Tagify(input,{
|
||||
originalInputValueFormat: valuesArr => valuesArr.map(item => item.value).join(', ')
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
@@ -4,8 +4,6 @@
|
||||
<!-- include required css cdn link through html here -->
|
||||
|
||||
{% 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 @@
|
||||
<div class="statbox widget box box-shadow">
|
||||
<div class="widget-content widget-content-area">
|
||||
|
||||
<form method="POST" enctype="multipart/form-data" novalidate>
|
||||
<form method="POST" enctype="multipart/form-data" id="venueForm">
|
||||
{% csrf_token %}
|
||||
{% include 'includes/dynamic_template_form.html' with form=form %}
|
||||
<div class="mt-4 mb-0">
|
||||
@@ -56,64 +54,105 @@
|
||||
<!-- include required css cdn link through html here -->
|
||||
|
||||
{% 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" %}
|
||||
|
||||
|
||||
<script>
|
||||
/**
|
||||
* ===================================
|
||||
* Blog Description Editor
|
||||
* ===================================
|
||||
*/
|
||||
var quill = new Quill('#description', {
|
||||
modules: {
|
||||
toolbar: [
|
||||
[{ header: [1, 2, false] }],
|
||||
['bold', 'italic', 'underline'],
|
||||
['image', 'code-block']
|
||||
]
|
||||
},
|
||||
placeholder: 'Write description...',
|
||||
theme: 'snow' // or 'bubble'
|
||||
});
|
||||
|
||||
$(document).ready(function(){
|
||||
// initialize filepond
|
||||
var image = initializeFilePond('id_image');
|
||||
var imageUrl = "{% if form.image.value %}{{ form.image.value.url }}{% endif %}";
|
||||
if (imageUrl){
|
||||
image.addFile(imageUrl)
|
||||
}
|
||||
|
||||
/**
|
||||
* ====================
|
||||
* File Pond
|
||||
* ====================
|
||||
*/
|
||||
// Validate the form
|
||||
$("#venueForm").validate({
|
||||
rules: {
|
||||
principal: {
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
required: true,
|
||||
noSpace: true
|
||||
},
|
||||
address: {
|
||||
required: true
|
||||
},
|
||||
image: {
|
||||
required: true,
|
||||
accept: "image/*"
|
||||
},
|
||||
latitude: {
|
||||
required: true,
|
||||
number: true,
|
||||
},
|
||||
longitude: {
|
||||
required: true,
|
||||
number: true,
|
||||
}
|
||||
},
|
||||
messages: {
|
||||
principal: {
|
||||
required: "Please select a principal"
|
||||
},
|
||||
title: {
|
||||
required: "Please enter a title"
|
||||
},
|
||||
address: {
|
||||
required: "Please enter an address"
|
||||
},
|
||||
image: {
|
||||
required: "Please upload an image",
|
||||
accept: "Please upload a valid image file"
|
||||
},
|
||||
latitude: {
|
||||
required: "Please enter a latitude",
|
||||
number: "Please enter a valid number",
|
||||
},
|
||||
longitude: {
|
||||
required: "Please enter a longitude",
|
||||
number: "Please enter a valid number",
|
||||
}
|
||||
},
|
||||
errorElement: "div",
|
||||
errorPlacement: function(error, element) {
|
||||
error.addClass("invalid-feedback");
|
||||
if (element.prop("type") === "checkbox") {
|
||||
error.insertAfter(element.next("label"));
|
||||
} else {
|
||||
error.insertAfter(element);
|
||||
}
|
||||
},
|
||||
highlight: function(element) {
|
||||
$(element).addClass("is-invalid").removeClass("is-valid");
|
||||
},
|
||||
unhighlight: function(element) {
|
||||
$(element).addClass("is-valid").removeClass("is-invalid");
|
||||
},
|
||||
submitHandler: function(form) {
|
||||
console.log("Form submission is valid");
|
||||
form.submit();
|
||||
}
|
||||
});
|
||||
|
||||
// We want to preview images, so we register
|
||||
// the Image Preview plugin, We also register
|
||||
// exif orientation (to correct mobile image
|
||||
// orientation) and size validation, to prevent
|
||||
// large files from being added
|
||||
FilePond.registerPlugin(
|
||||
FilePondPluginImagePreview,
|
||||
FilePondPluginImageExifOrientation,
|
||||
FilePondPluginFileValidateSize,
|
||||
// FilePondPluginImageEdit
|
||||
);
|
||||
|
||||
// Select the file input and use
|
||||
// create() to turn it into a pond
|
||||
var ecommerce = FilePond.create(document.querySelector('.file-upload-multiple'));
|
||||
|
||||
|
||||
/**
|
||||
* =====================
|
||||
* Blog Tags
|
||||
* =====================
|
||||
*/
|
||||
// The DOM element you wish to replace with Tagify
|
||||
var input = document.querySelector('#id_tags');
|
||||
|
||||
// initialize Tagify on the above input node reference
|
||||
new Tagify(input, {
|
||||
originalInputValueFormat: valuesArr => valuesArr.map(item => item.value).join(', ')
|
||||
})
|
||||
|
||||
// Function to initialize FilePond
|
||||
function initializeFilePond(selector, allowMultiple = false) {
|
||||
FilePond.registerPlugin(
|
||||
FilePondPluginImagePreview,
|
||||
FilePondPluginImageExifOrientation,
|
||||
FilePondPluginFileValidateSize
|
||||
);
|
||||
return FilePond.create(document.getElementById(selector),{
|
||||
allowMultiple: allowMultiple,
|
||||
storeAsFile: true,
|
||||
dropOnPage: true
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user