feat(customer): customer cred and postcode for address

This commit is contained in:
bobbyvish
2024-12-20 19:43:44 +05:30
parent 3425d82891
commit 24949ade3e
15 changed files with 301 additions and 92 deletions

View File

@@ -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),
),
]

View File

@@ -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,

View File

@@ -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"),

View File

@@ -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(

View File

@@ -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()

View File

@@ -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

View File

@@ -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)

View File

@@ -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",

View 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),
),
]

View File

@@ -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"

View File

@@ -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']:

View File

@@ -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(),
),
]

View File

@@ -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

View File

@@ -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 %}

View File

@@ -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">