feat(customer): customer cred and postcode for address
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.2 on 2024-12-20 12:15
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('accounts', '0015_iamprincipal_twitter_profile'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='iamprincipalextendeddata',
|
||||||
|
name='encrypted_pass',
|
||||||
|
field=models.TextField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -13,6 +13,7 @@ from django.utils.text import slugify
|
|||||||
from phonenumber_field.modelfields import PhoneNumberField
|
from phonenumber_field.modelfields import PhoneNumberField
|
||||||
|
|
||||||
# from manage_subscriptions.models import Subscription
|
# from manage_subscriptions.models import Subscription
|
||||||
|
|
||||||
from goodtimes.utils import RandomGenerator
|
from goodtimes.utils import RandomGenerator
|
||||||
from .resource_action import (
|
from .resource_action import (
|
||||||
PRINCIPAL_TYPE_EVENT_USER,
|
PRINCIPAL_TYPE_EVENT_USER,
|
||||||
@@ -333,9 +334,16 @@ class IAmPrincipal(AbstractUser):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.email}"
|
return f"{self.email}"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generate_random_password():
|
||||||
|
"""Generate a password in the format 'GoodTimes@xxxx'."""
|
||||||
|
random_number = random.randint(1000, 9999) # Generate a 4-digit random number
|
||||||
|
return f"GoodTimes@{random_number}"
|
||||||
|
|
||||||
|
|
||||||
class IAmPrincipalExtendedData(models.Model):
|
class IAmPrincipalExtendedData(models.Model):
|
||||||
|
|
||||||
principal = models.OneToOneField(
|
principal = models.OneToOneField(
|
||||||
IAmPrincipal,
|
IAmPrincipal,
|
||||||
related_name="extended_data",
|
related_name="extended_data",
|
||||||
@@ -356,7 +364,7 @@ class IAmPrincipalExtendedData(models.Model):
|
|||||||
help_text="The date and time when the account was transferred to the user."
|
help_text="The date and time when the account was transferred to the user."
|
||||||
)
|
)
|
||||||
pwd_changed_post_transfer = models.BooleanField(default=False, help_text="Indicates if the user changed their password after the account was transferred.")
|
pwd_changed_post_transfer = models.BooleanField(default=False, help_text="Indicates if the user changed their password after the account was transferred.")
|
||||||
|
encrypted_pass = models.TextField(blank=True, null=True)
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "iam_principal_extended_data"
|
db_table = "iam_principal_extended_data"
|
||||||
|
|
||||||
@@ -368,6 +376,12 @@ class IAmPrincipalExtendedData(models.Model):
|
|||||||
self.transferred_on = datetime.datetime.now()
|
self.transferred_on = datetime.datetime.now()
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def decrypted_field(self):
|
||||||
|
from goodtimes.services import Encryptor
|
||||||
|
encryptor = Encryptor()
|
||||||
|
return encryptor.decrypt(self.encrypted_pass)
|
||||||
|
|
||||||
class IAmPrincipalResourceLink(models.Model):
|
class IAmPrincipalResourceLink(models.Model):
|
||||||
principal = models.ForeignKey(
|
principal = models.ForeignKey(
|
||||||
IAmPrincipal,
|
IAmPrincipal,
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ urlpatterns = [
|
|||||||
path('principal/role/delete/<int:pk>/', views.AppRoleDeleteView.as_view(), name="role_delete"),
|
path('principal/role/delete/<int:pk>/', views.AppRoleDeleteView.as_view(), name="role_delete"),
|
||||||
|
|
||||||
path('customer/', views.CustomerListView.as_view(), name="customer_list"),
|
path('customer/', views.CustomerListView.as_view(), name="customer_list"),
|
||||||
|
path('customer/get-decrypted-password/<int:customer_id>/', views.GetDecryptedPasswordView.as_view(), name='get_decrypted_password'),
|
||||||
path('customer/add/', views.CustomerCreateView.as_view(), name="customer_add"),
|
path('customer/add/', views.CustomerCreateView.as_view(), name="customer_add"),
|
||||||
path('customer/edit/<int:pk>/', views.CustomerUpdateView.as_view(), name="customer_edit"),
|
path('customer/edit/<int:pk>/', views.CustomerUpdateView.as_view(), name="customer_edit"),
|
||||||
path('customer/detail/<int:pk>/', views.CustomerDetailView.as_view(), name="customer_detail"),
|
path('customer/detail/<int:pk>/', views.CustomerDetailView.as_view(), name="customer_detail"),
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ from django.utils import timezone
|
|||||||
import phonenumbers
|
import phonenumbers
|
||||||
from accounts import permission
|
from accounts import permission
|
||||||
from goodtimes import constants
|
from goodtimes import constants
|
||||||
from goodtimes.services import EmailService
|
from goodtimes.services import EmailService, Encryptor
|
||||||
from goodtimes.utils import JsonResponseUtil
|
from goodtimes.utils import JsonResponseUtil
|
||||||
from manage_events.models import EventCategory, PrincipalPreference
|
from manage_events.models import EventCategory, PrincipalPreference
|
||||||
from manage_referrals.models import ReferralCode
|
from manage_referrals.models import ReferralCode
|
||||||
@@ -621,6 +621,12 @@ class CustomerCreateView(LoginRequiredMixin, generic.View):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
|
random_password = IAmPrincipal.generate_random_password()
|
||||||
|
|
||||||
|
# Encrypt the password
|
||||||
|
encryptor = Encryptor()
|
||||||
|
encrypted_password = encryptor.encrypt(random_password)
|
||||||
|
|
||||||
# save principal data
|
# save principal data
|
||||||
principal_obj = IAmPrincipal.objects.create(
|
principal_obj = IAmPrincipal.objects.create(
|
||||||
email=form.cleaned_data.get('email'),
|
email=form.cleaned_data.get('email'),
|
||||||
@@ -628,7 +634,7 @@ class CustomerCreateView(LoginRequiredMixin, generic.View):
|
|||||||
last_name=form.cleaned_data.get('last_name'),
|
last_name=form.cleaned_data.get('last_name'),
|
||||||
business_name=form.cleaned_data.get('business_name'),
|
business_name=form.cleaned_data.get('business_name'),
|
||||||
phone_no=form.cleaned_data.get('phone_no'),
|
phone_no=form.cleaned_data.get('phone_no'),
|
||||||
password=make_password("goodtimes#2024"),
|
password=make_password(random_password),
|
||||||
username=form.cleaned_data.get("email"),
|
username=form.cleaned_data.get("email"),
|
||||||
email_verified=True,
|
email_verified=True,
|
||||||
register_complete=True,
|
register_complete=True,
|
||||||
@@ -651,6 +657,7 @@ class CustomerCreateView(LoginRequiredMixin, generic.View):
|
|||||||
IAmPrincipalExtendedData.objects.create(
|
IAmPrincipalExtendedData.objects.create(
|
||||||
principal=principal_obj,
|
principal=principal_obj,
|
||||||
is_onboarded=True,
|
is_onboarded=True,
|
||||||
|
encrypted_pass=encrypted_password, # Save encrypted password
|
||||||
)
|
)
|
||||||
|
|
||||||
# save principal preferences record
|
# save principal preferences record
|
||||||
@@ -665,7 +672,6 @@ class CustomerCreateView(LoginRequiredMixin, generic.View):
|
|||||||
is_paid=True,
|
is_paid=True,
|
||||||
subscription=free_subscription
|
subscription=free_subscription
|
||||||
)
|
)
|
||||||
|
|
||||||
messages.success(self.request, constants.REGISTRATION_SUCCESS)
|
messages.success(self.request, constants.REGISTRATION_SUCCESS)
|
||||||
return redirect(self.success_url)
|
return redirect(self.success_url)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -862,6 +868,27 @@ class CustomerListView(LoginRequiredMixin, generic.ListView):
|
|||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["page_name"] = self.page_name
|
context["page_name"] = self.page_name
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class GetDecryptedPasswordView(generic.View):
|
||||||
|
def get(self, request, customer_id):
|
||||||
|
try:
|
||||||
|
# Fetch the extended data for the customer
|
||||||
|
extended_data = IAmPrincipalExtendedData.objects.get(principal_id=customer_id)
|
||||||
|
|
||||||
|
if not extended_data.encrypted_pass:
|
||||||
|
return JsonResponse({"success": False, "message": "No password found."})
|
||||||
|
|
||||||
|
# Use Encryptor to decrypt the password
|
||||||
|
encryptor = Encryptor()
|
||||||
|
decrypted_password = encryptor.decrypt(extended_data.encrypted_pass)
|
||||||
|
|
||||||
|
return JsonResponse({"success": True, "decrypted_password": decrypted_password})
|
||||||
|
except IAmPrincipalExtendedData.DoesNotExist:
|
||||||
|
return JsonResponse({"success": False, "message": "Customer not found."})
|
||||||
|
except Exception as e:
|
||||||
|
return JsonResponse({"success": False, "message": str(e)})
|
||||||
|
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from openpyxl import Workbook, load_workbook
|
from openpyxl import Workbook, load_workbook
|
||||||
@@ -1022,7 +1049,7 @@ class CustomerTransferView(LoginRequiredMixin, generic.View):
|
|||||||
|
|
||||||
# Send the email
|
# Send the email
|
||||||
try:
|
try:
|
||||||
temp_password = "goodtimes#2024"
|
temp_password = "GoodTimes@2025"
|
||||||
principal_obj.password = make_password(temp_password)
|
principal_obj.password = make_password(temp_password)
|
||||||
principal_obj.save()
|
principal_obj.save()
|
||||||
email_service.load_template(
|
email_service.load_template(
|
||||||
@@ -1159,12 +1186,18 @@ class CustomerImportView(LoginRequiredMixin, generic.View):
|
|||||||
error_log.append(f"Row {idx}: One or more preferences are invalid.")
|
error_log.append(f"Row {idx}: One or more preferences are invalid.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
random_password = IAmPrincipal.generate_random_password()
|
||||||
|
|
||||||
|
# Encrypt the password
|
||||||
|
encryptor = Encryptor()
|
||||||
|
encrypted_password = encryptor.encrypt(random_password)
|
||||||
|
|
||||||
# collect the principals
|
# collect the principals
|
||||||
principal = IAmPrincipal(
|
principal = IAmPrincipal(
|
||||||
first_name=first_name.strip().capitalize(),
|
first_name=first_name.strip().capitalize(),
|
||||||
last_name=last_name.strip().capitalize(),
|
last_name=last_name.strip().capitalize(),
|
||||||
email=email.strip(),
|
email=email.strip(),
|
||||||
password=make_password("goodtimes#2024"),
|
password=make_password(random_password),
|
||||||
username=email.strip(),
|
username=email.strip(),
|
||||||
email_verified=True,
|
email_verified=True,
|
||||||
register_complete=True,
|
register_complete=True,
|
||||||
@@ -1207,7 +1240,7 @@ class CustomerImportView(LoginRequiredMixin, generic.View):
|
|||||||
ReferralCode.create_referral_code_for_user_manager(principal=principal, principal_type=principal_type)
|
ReferralCode.create_referral_code_for_user_manager(principal=principal, principal_type=principal_type)
|
||||||
|
|
||||||
# Create IAmPrincipalExtendedData record
|
# Create IAmPrincipalExtendedData record
|
||||||
IAmPrincipalExtendedData.objects.create(principal=principal, is_onboarded=True)
|
IAmPrincipalExtendedData.objects.create(principal=principal, is_onboarded=True,encrypted_pass=encrypted_password,)
|
||||||
|
|
||||||
# Create PrincipalSubscription record
|
# Create PrincipalSubscription record
|
||||||
subscription = PrincipalSubscription(
|
subscription = PrincipalSubscription(
|
||||||
|
|||||||
@@ -726,9 +726,6 @@ class FacebookPoster:
|
|||||||
self.facebook_api = facebook_api
|
self.facebook_api = facebook_api
|
||||||
|
|
||||||
def post_photo(self, image_url, caption):
|
def post_photo(self, image_url, caption):
|
||||||
if not self.facebook_api.authenticate():
|
|
||||||
print("Authentication failed. Please try again.")
|
|
||||||
return {'success': False, 'message': 'Error posting photo. Authenticate failed'}
|
|
||||||
result = self.facebook_api.post_photo(image_url, caption)
|
result = self.facebook_api.post_photo(image_url, caption)
|
||||||
if not result:
|
if not result:
|
||||||
return {'success': False, 'message': 'Error posting photo in Facebook'}
|
return {'success': False, 'message': 'Error posting photo in Facebook'}
|
||||||
@@ -1130,4 +1127,18 @@ class StripeService:
|
|||||||
)
|
)
|
||||||
return {'success': True, 'data': subscription}
|
return {'success': True, 'data': subscription}
|
||||||
except stripe.error.StripeError as e:
|
except stripe.error.StripeError as e:
|
||||||
return {'success': False, 'message': f'Error cancelling subscription auto-renewal: {e}'}
|
|
||||||
|
return {'success': False, 'message': f'Error cancelling subscription auto-renewal: {e}'}
|
||||||
|
|
||||||
|
from cryptography.fernet import Fernet
|
||||||
|
class Encryptor:
|
||||||
|
def __init__(self):
|
||||||
|
self.key = "paMSf3Ny8KAMs1tRLcVOQQhRxTnInHLwP7WtVdm8O_4="
|
||||||
|
self.fernet = Fernet(self.key)
|
||||||
|
|
||||||
|
def encrypt(self, plaintext):
|
||||||
|
return self.fernet.encrypt(plaintext.encode()).decode()
|
||||||
|
|
||||||
|
def decrypt(self, encrypted_text):
|
||||||
|
return self.fernet.decrypt(encrypted_text.encode()).decode()
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ THIRD_PARTY_APPS = [
|
|||||||
"taggit",
|
"taggit",
|
||||||
"django_quill",
|
"django_quill",
|
||||||
"corsheaders",
|
"corsheaders",
|
||||||
|
'django_extensions',
|
||||||
"allauth",
|
"allauth",
|
||||||
"allauth.account",
|
"allauth.account",
|
||||||
"allauth.socialaccount",
|
"allauth.socialaccount",
|
||||||
@@ -211,6 +212,7 @@ TIME_FORMAT = "H:i p"
|
|||||||
|
|
||||||
# otp expire time limit
|
# otp expire time limit
|
||||||
OTP_EXPIRE_TIME = 1 # mins
|
OTP_EXPIRE_TIME = 1 # mins
|
||||||
|
DEFAULT_CHARSET = 'utf-8'
|
||||||
|
|
||||||
|
|
||||||
# Default primary key field type
|
# Default primary key field type
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ class CreateVenueApi(APIView):
|
|||||||
def post(self, request):
|
def post(self, request):
|
||||||
|
|
||||||
data = request.data.copy()
|
data = request.data.copy()
|
||||||
|
print("prindata is ", data)
|
||||||
|
|
||||||
# Convert latitude and longitude to float and round to 8 decimal places
|
# Convert latitude and longitude to float and round to 8 decimal places
|
||||||
data["latitude"] = round(float(data["latitude"]), 8)
|
data["latitude"] = round(float(data["latitude"]), 8)
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ class VenueForm(forms.ModelForm):
|
|||||||
required=True
|
required=True
|
||||||
)
|
)
|
||||||
image = forms.ImageField(required=True)
|
image = forms.ImageField(required=True)
|
||||||
|
postcode = forms.CharField(required=True, max_length=10)
|
||||||
latitude = forms.DecimalField(
|
latitude = forms.DecimalField(
|
||||||
widget=forms.NumberInput()
|
widget=forms.NumberInput()
|
||||||
)
|
)
|
||||||
@@ -161,6 +162,7 @@ class VenueForm(forms.ModelForm):
|
|||||||
"principal",
|
"principal",
|
||||||
"title",
|
"title",
|
||||||
"address",
|
"address",
|
||||||
|
"postcode",
|
||||||
"image",
|
"image",
|
||||||
"latitude",
|
"latitude",
|
||||||
"longitude",
|
"longitude",
|
||||||
|
|||||||
18
manage_events/migrations/0017_venue_postcode.py
Normal file
18
manage_events/migrations/0017_venue_postcode.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.2 on 2024-12-20 09:29
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('manage_events', '0016_freeusagefeaturelimit'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='venue',
|
||||||
|
name='postcode',
|
||||||
|
field=models.CharField(blank=True, max_length=20, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -51,13 +51,14 @@ class Venue(BaseModel):
|
|||||||
longitude = models.DecimalField(
|
longitude = models.DecimalField(
|
||||||
max_digits=14, decimal_places=8, blank=True, null=True
|
max_digits=14, decimal_places=8, blank=True, null=True
|
||||||
)
|
)
|
||||||
|
postcode = models.CharField(max_length=20, blank=True, null=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
class AgeGroups(BaseModel):
|
class AgeGroups(BaseModel):
|
||||||
name = models.CharField(max_length=10, unique=True)
|
name = models.CharField(max_length=10, unique=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "age_group"
|
db_table = "age_group"
|
||||||
|
|
||||||
|
|||||||
@@ -599,12 +599,17 @@ class SocialMediaPostView(generic.View):
|
|||||||
|
|
||||||
image_url = request.build_absolute_uri(event.image.url)
|
image_url = request.build_absolute_uri(event.image.url)
|
||||||
if platform in ['facebook', 'all']:
|
if platform in ['facebook', 'all']:
|
||||||
facebook_api = FacebookAPI()
|
try:
|
||||||
facebook_poster = FacebookPoster(facebook_api)
|
print("facebook is called")
|
||||||
result = facebook_poster.post_photo(image_url, caption)
|
facebook_api = FacebookAPI()
|
||||||
if result["success"]:
|
facebook_poster = FacebookPoster(facebook_api)
|
||||||
success_messages.append("Posted to Facebook successfully")
|
result = facebook_poster.post_photo(image_url, caption)
|
||||||
else:
|
if result["success"]:
|
||||||
|
success_messages.append("Posted to Facebook successfully")
|
||||||
|
else:
|
||||||
|
errors.append("Fail to post on Facebook")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"facebook error {e}")
|
||||||
errors.append("Fail to post on Facebook")
|
errors.append("Fail to post on Facebook")
|
||||||
|
|
||||||
if platform in ['instagram', 'all']:
|
if platform in ['instagram', 'all']:
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 5.0.2 on 2024-12-20 09:29
|
||||||
|
|
||||||
|
import django_quill.fields
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('manage_subscriptions', '0014_alter_subscription_long_description'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='subscription',
|
||||||
|
name='long_description',
|
||||||
|
field=django_quill.fields.QuillField(),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -75,7 +75,7 @@ stripe==8.2.0
|
|||||||
tqdm==4.66.2
|
tqdm==4.66.2
|
||||||
tweepy==4.14.0
|
tweepy==4.14.0
|
||||||
Twisted==23.10.0
|
Twisted==23.10.0
|
||||||
twisted-iocpsupport==1.0.4
|
# twisted-iocpsupport==1.0.4
|
||||||
txaio==23.1.1
|
txaio==23.1.1
|
||||||
typing_extensions==4.9.0
|
typing_extensions==4.9.0
|
||||||
tzdata==2024.1
|
tzdata==2024.1
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
{% extends 'layout/base_template.html' %}
|
{% extends 'layout/base_template.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% block stylesheet %}
|
{% block stylesheet %}
|
||||||
<!-- include required css cdn link through html here -->
|
<!-- include required css cdn link through html here -->
|
||||||
{% include "cdn_through_html/datatable_cdn_css.html" %}
|
{% include "cdn_through_html/datatable_cdn_css.html" %}
|
||||||
|
{% include "cdn_through_html/modal_cdn_css.html" %}
|
||||||
|
{% include "cdn_through_html/sweetalert2_cdn_css.html" %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -19,7 +21,7 @@
|
|||||||
<a class="btn btn-dark mb-2 me-md-4" href="{% url 'accounts:download_excel_template' %}">
|
<a class="btn btn-dark mb-2 me-md-4" href="{% url 'accounts:download_excel_template' %}">
|
||||||
Download Excel Template
|
Download Excel Template
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="btn btn-dark mb-2 me-md-4" href="{% url 'accounts:import_customer_data' %}">
|
<a class="btn btn-dark mb-2 me-md-4" href="{% url 'accounts:import_customer_data' %}">
|
||||||
Import
|
Import
|
||||||
</a>
|
</a>
|
||||||
@@ -27,12 +29,12 @@
|
|||||||
<a class="btn btn-dark mb-2 me-md-4" href="{% url 'accounts:export_customer_data' %}">
|
<a class="btn btn-dark mb-2 me-md-4" href="{% url 'accounts:export_customer_data' %}">
|
||||||
Export
|
Export
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="btn btn-primary mb-2 me-4" href="{% url 'accounts:customer_add' %}">Add Customer</a>
|
<a class="btn btn-primary mb-2 me-4" href="{% url 'accounts:customer_add' %}">Add Customer</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="row layout-spacing">
|
<div class="row layout-spacing">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="statbox widget box box-shadow">
|
<div class="statbox widget box box-shadow">
|
||||||
@@ -46,39 +48,39 @@
|
|||||||
<th class="checkbox-column text-center sorting_asc" tabindex="0"
|
<th class="checkbox-column text-center sorting_asc" tabindex="0"
|
||||||
aria-controls="style-3" rowspan="1" colspan="1" aria-sort="ascending"
|
aria-controls="style-3" rowspan="1" colspan="1" aria-sort="ascending"
|
||||||
style="width: 69.2656px;"> Record Id </th>
|
style="width: 69.2656px;"> Record Id </th>
|
||||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
|
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
|
||||||
style="width: 44.2344px;">Image</th>
|
rowspan="1" style="width: 44.2344px;">Image</th>
|
||||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
|
||||||
style="width: 79.7969px;">First Name</th>
|
rowspan="1" colspan="1" style="width: 79.7969px;">First Name</th>
|
||||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
|
||||||
style="width: 77.3281px;">Last Name</th>
|
rowspan="1" colspan="1" style="width: 77.3281px;">Last Name</th>
|
||||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
|
||||||
style="width: 143.516px;">Email</th>
|
rowspan="1" colspan="1" style="width: 143.516px;">Email</th>
|
||||||
<!-- <th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
|
||||||
style="width: 98.875px;">Mobile No.</th> -->
|
rowspan="1" colspan="1" style="width: 98.875px;">#</th>
|
||||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
|
||||||
style="width: 98.875px;">Principal Type</th>
|
rowspan="1" colspan="1" style="width: 98.875px;">Principal Type</th>
|
||||||
<!-- <th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
<!-- <th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
||||||
style="width: 98.875px;">Phone Verified</th> -->
|
style="width: 98.875px;">Phone Verified</th> -->
|
||||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
|
||||||
style="width: 98.875px;">Email Verified</th>
|
rowspan="1" colspan="1" style="width: 98.875px;">Email Verified</th>
|
||||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
|
||||||
style="width: 98.875px;">Referral Count</th>
|
rowspan="1" colspan="1" style="width: 98.875px;">Referral Count</th>
|
||||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
|
||||||
style="width: 98.875px;">Onboarded by Admin</th>
|
rowspan="1" colspan="1" style="width: 98.875px;">Onboarded by Admin</th>
|
||||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
|
||||||
style="width: 98.875px;">Transferred to Customer</th>
|
rowspan="1" colspan="1" style="width: 98.875px;">Transferred to Customer
|
||||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
</th>
|
||||||
style="width: 98.875px;">Created On</th>
|
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
|
||||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
rowspan="1" colspan="1" style="width: 98.875px;">Created On</th>
|
||||||
style="width: 98.875px;">Modified On</th>
|
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
|
||||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
rowspan="1" colspan="1" style="width: 98.875px;">Modified On</th>
|
||||||
style="width: 98.875px;">Active</th>
|
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
|
||||||
|
rowspan="1" colspan="1" style="width: 98.875px;">Active</th>
|
||||||
<!-- <th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
<!-- <th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
||||||
style="width: 98.875px;">Delete</th> -->
|
style="width: 98.875px;">Delete</th> -->
|
||||||
<th class="text-center dt-no-sorting" tabindex="0"
|
<th class="text-center dt-no-sorting" tabindex="0" aria-controls="style-3"
|
||||||
aria-controls="style-3" rowspan="1" colspan="1"
|
rowspan="1" colspan="1" style="width: 51.625px;">Action</th>
|
||||||
style="width: 51.625px;">Action</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -92,33 +94,43 @@
|
|||||||
<td>{{ data_obj.first_name }}</td>
|
<td>{{ data_obj.first_name }}</td>
|
||||||
<td>{{ data_obj.last_name }}</td>
|
<td>{{ data_obj.last_name }}</td>
|
||||||
<td>{{ data_obj.email }}</td>
|
<td>{{ data_obj.email }}</td>
|
||||||
<!-- <td>{{ data_obj.phone_no }}</td> -->
|
<td>
|
||||||
|
{% if data_obj.extended_data %}
|
||||||
|
<button class="btn btn-primary btn-sm view-password-btn"
|
||||||
|
data-id="{{ data_obj.id }}" data-email="{{ data_obj.email }}">
|
||||||
|
View
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td>{{ data_obj.principal_type.name }}</td>
|
<td>{{ data_obj.principal_type.name }}</td>
|
||||||
<!-- <td>{{ data_obj.phone_verified }}</td> -->
|
<!-- <td>{{ data_obj.phone_verified }}</td> -->
|
||||||
<td>{{ data_obj.email_verified }}</td>
|
<td>{{ data_obj.email_verified }}</td>
|
||||||
<td>{{ data_obj.referral_count }}</td>
|
<td>{{ data_obj.referral_count }}</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<span class="shadow-none badge {% if data_obj.extended_data and data_obj.extended_data.is_onboarded %}badge-primary{% else %}badge-danger{% endif %}">
|
<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 %}
|
{% if data_obj.extended_data %}
|
||||||
{{ data_obj.extended_data.is_onboarded }}
|
{{ data_obj.extended_data.is_onboarded }}
|
||||||
{% else %}
|
{% else %}
|
||||||
False
|
False
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<span class="shadow-none badge {% if data_obj.extended_data and data_obj.extended_data.is_transferred %}badge-primary{% else %}badge-danger{% endif %}">
|
<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 %}
|
{% if data_obj.extended_data %}
|
||||||
{{ data_obj.extended_data.is_transferred }}
|
{{ data_obj.extended_data.is_transferred }}
|
||||||
{% else %}
|
{% else %}
|
||||||
False
|
False
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ data_obj.created_on }}</td>
|
<td>{{ data_obj.created_on }}</td>
|
||||||
<td>{{ data_obj.modified_on }}</td>
|
<td>{{ data_obj.modified_on }}</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<span class="shadow-none badge {% if data_obj.is_active %}badge-primary{% else %}badge-danger{% endif %}">
|
<span
|
||||||
|
class="shadow-none badge {% if data_obj.is_active %}badge-primary{% else %}badge-danger{% endif %}">
|
||||||
{{ data_obj.is_active }}
|
{{ data_obj.is_active }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
@@ -129,11 +141,12 @@
|
|||||||
</td> -->
|
</td> -->
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<ul class="table-controls">
|
<ul class="table-controls">
|
||||||
<li><a href="{% url 'accounts:customer_edit' data_obj.id%}" class="bs-tooltip"
|
<li><a href="{% url 'accounts:customer_edit' data_obj.id%}"
|
||||||
data-bs-toggle="tooltip" data-bs-placement="top" title=""
|
class="bs-tooltip" data-bs-toggle="tooltip"
|
||||||
data-original-title="Edit" data-bs-original-title="Edit"
|
data-bs-placement="top" title="" data-original-title="Edit"
|
||||||
aria-label="Edit"><svg xmlns="http://www.w3.org/2000/svg"
|
data-bs-original-title="Edit" aria-label="Edit"><svg
|
||||||
width="24" height="24" viewBox="0 0 24 24" fill="none"
|
xmlns="http://www.w3.org/2000/svg" width="24"
|
||||||
|
height="24" viewBox="0 0 24 24" fill="none"
|
||||||
stroke="currentColor" stroke-width="2"
|
stroke="currentColor" stroke-width="2"
|
||||||
stroke-linecap="round" stroke-linejoin="round"
|
stroke-linecap="round" stroke-linejoin="round"
|
||||||
class="feather feather-edit-2 p-1 br-8 mb-1">
|
class="feather feather-edit-2 p-1 br-8 mb-1">
|
||||||
@@ -141,9 +154,20 @@
|
|||||||
d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z">
|
d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z">
|
||||||
</path>
|
</path>
|
||||||
</svg></a></li>
|
</svg></a></li>
|
||||||
<li><a href="{% url 'accounts:customer_detail' data_obj.id%}" class="bs-tooltip" data-bs-toggle="tooltip" data-bs-placement="top" title="" data-original-title="View" data-bs-original-title="View" aria-label="View">
|
<li><a href="{% url 'accounts:customer_detail' data_obj.id%}"
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-eye"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>
|
class="bs-tooltip" data-bs-toggle="tooltip"
|
||||||
</a></li>
|
data-bs-placement="top" title="" data-original-title="View"
|
||||||
|
data-bs-original-title="View" aria-label="View">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24"
|
||||||
|
height="24" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
class="feather feather-eye">
|
||||||
|
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z">
|
||||||
|
</path>
|
||||||
|
<circle cx="12" cy="12" r="3"></circle>
|
||||||
|
</svg>
|
||||||
|
</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -159,31 +183,88 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="modal fade" id="passwordModal" tabindex="-1" role="dialog" aria-labelledby="passwordModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="passwordModalLabel">Customer Details</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p><strong>Email:</strong> <span id="modalEmail"></span></p>
|
||||||
|
<p><strong>Password:</strong> <span id="modalPassword"></span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
{% block javascript %}
|
{% block javascript %}
|
||||||
<!-- include required js cdn link through html here -->
|
<!-- include required js cdn link through html here -->
|
||||||
{% include "cdn_through_html/datatable_cdn_js.html" %}
|
{% include "cdn_through_html/datatable_cdn_js.html" %}
|
||||||
|
{% include "cdn_through_html/sweetalert2_cdn_js.html" %}
|
||||||
<script>
|
|
||||||
c3 = $('#style-3').DataTable({
|
<script>
|
||||||
"dom": "<'dt--top-section'<'row'<'col-12 col-sm-6 d-flex justify-content-sm-start justify-content-center'l><'col-12 col-sm-6 d-flex justify-content-sm-end justify-content-center mt-sm-0 mt-3'f>>>" +
|
c3 = $('#style-3').DataTable({
|
||||||
"<'table-responsive'tr>" +
|
"dom": "<'dt--top-section'<'row'<'col-12 col-sm-6 d-flex justify-content-sm-start justify-content-center'l><'col-12 col-sm-6 d-flex justify-content-sm-end justify-content-center mt-sm-0 mt-3'f>>>" +
|
||||||
"<'dt--bottom-section d-sm-flex justify-content-sm-between text-center'<'dt--pages-count mb-sm-0 mb-3'i><'dt--pagination'p>>",
|
"<'table-responsive'tr>" +
|
||||||
"oLanguage": {
|
"<'dt--bottom-section d-sm-flex justify-content-sm-between text-center'<'dt--pages-count mb-sm-0 mb-3'i><'dt--pagination'p>>",
|
||||||
"oPaginate": { "sPrevious": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-left"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>', "sNext": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-right"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>' },
|
"oLanguage": {
|
||||||
"sInfo": "Showing page _PAGE_ of _PAGES_",
|
"oPaginate": { "sPrevious": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-left"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>', "sNext": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-right"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>' },
|
||||||
"sSearch": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>',
|
"sInfo": "Showing page _PAGE_ of _PAGES_",
|
||||||
"sSearchPlaceholder": "Search...",
|
"sSearch": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>',
|
||||||
"sLengthMenu": "Results : _MENU_",
|
"sSearchPlaceholder": "Search...",
|
||||||
},
|
"sLengthMenu": "Results : _MENU_",
|
||||||
"order": [[ 0, "desc" ]],
|
},
|
||||||
"stripeClasses": [],
|
"order": [[0, "desc"]],
|
||||||
"lengthMenu": [5, 10, 20, 50],
|
"stripeClasses": [],
|
||||||
"pageLength": 10
|
"lengthMenu": [5, 10, 20, 50],
|
||||||
|
"pageLength": 10
|
||||||
|
});
|
||||||
|
|
||||||
|
multiCheck(c3);
|
||||||
|
|
||||||
|
var viewUrl = "{% url 'accounts:get_decrypted_password' customer_id=0 %}"
|
||||||
|
$(".view-password-btn").on("click", function () {
|
||||||
|
const customerId = $(this).data("id");
|
||||||
|
const email = $(this).data("email");
|
||||||
|
|
||||||
|
// Make the AJAX call
|
||||||
|
$.ajax({
|
||||||
|
// url: `accounts//get-decrypted-password/${customerId}/`,
|
||||||
|
url: viewUrl.replace('0',customerId),
|
||||||
|
type: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
success: function (response) {
|
||||||
|
if (response.success) {
|
||||||
|
// Populate modal with email and decrypted password
|
||||||
|
$("#modalEmail").text(email);
|
||||||
|
$("#modalPassword").text(response.decrypted_password);
|
||||||
|
|
||||||
|
// Show the modal
|
||||||
|
$("#passwordModal").modal("show");
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Error!',
|
||||||
|
text: response.message || "Failed to fetch the password. Please try again.",
|
||||||
|
icon: 'error',
|
||||||
|
showConfirmButton: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
console.error("AJAX Error:", error);
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Error!',
|
||||||
|
text: "An error occurred. Please try again.",
|
||||||
|
icon: 'error',
|
||||||
|
showConfirmButton: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
multiCheck(c3);
|
</script>
|
||||||
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -45,6 +45,8 @@
|
|||||||
aria-sort="ascending" style="width: 69.2656px;"> Title </th>
|
aria-sort="ascending" style="width: 69.2656px;"> Title </th>
|
||||||
<th class="checkbox-column sorting_asc" tabindex="0" aria-controls="style-3"
|
<th class="checkbox-column sorting_asc" tabindex="0" aria-controls="style-3"
|
||||||
aria-sort="ascending" style="width: 69.2656px;"> Address </th>
|
aria-sort="ascending" style="width: 69.2656px;"> Address </th>
|
||||||
|
<th class="checkbox-column sorting_asc" tabindex="0" aria-controls="style-3"
|
||||||
|
aria-sort="ascending" style="width: 69.2656px;"> Postcode </th>
|
||||||
<th class="checkbox-column sorting_asc" tabindex="0" aria-controls="style-3"
|
<th class="checkbox-column sorting_asc" tabindex="0" aria-controls="style-3"
|
||||||
aria-sort="ascending" style="width: 69.2656px;"> Latitude </th>
|
aria-sort="ascending" style="width: 69.2656px;"> Latitude </th>
|
||||||
<th class="checkbox-column sorting_asc" tabindex="0" aria-controls="style-3"
|
<th class="checkbox-column sorting_asc" tabindex="0" aria-controls="style-3"
|
||||||
@@ -68,6 +70,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>{{data_obj.title}}</td>
|
<td>{{data_obj.title}}</td>
|
||||||
<td>{{data_obj.address}}</td>
|
<td>{{data_obj.address}}</td>
|
||||||
|
<td>{{data_obj.postcode}}</td>
|
||||||
<td>{{data_obj.latitude}}</td>
|
<td>{{data_obj.latitude}}</td>
|
||||||
<td>{{data_obj.longitude}}</td>
|
<td>{{data_obj.longitude}}</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
|
|||||||
Reference in New Issue
Block a user