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 manage_subscriptions.models import Subscription
from goodtimes.utils import RandomGenerator
from .resource_action import (
PRINCIPAL_TYPE_EVENT_USER,
@@ -333,9 +334,16 @@ class IAmPrincipal(AbstractUser):
def __str__(self):
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):
principal = models.OneToOneField(
IAmPrincipal,
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."
)
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:
db_table = "iam_principal_extended_data"
@@ -368,6 +376,12 @@ class IAmPrincipalExtendedData(models.Model):
self.transferred_on = datetime.datetime.now()
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):
principal = models.ForeignKey(
IAmPrincipal,

View File

@@ -42,6 +42,7 @@ urlpatterns = [
path('principal/role/delete/<int:pk>/', views.AppRoleDeleteView.as_view(), name="role_delete"),
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/edit/<int:pk>/', views.CustomerUpdateView.as_view(), name="customer_edit"),
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
from accounts import permission
from goodtimes import constants
from goodtimes.services import EmailService
from goodtimes.services import EmailService, Encryptor
from goodtimes.utils import JsonResponseUtil
from manage_events.models import EventCategory, PrincipalPreference
from manage_referrals.models import ReferralCode
@@ -621,6 +621,12 @@ class CustomerCreateView(LoginRequiredMixin, generic.View):
try:
with transaction.atomic():
random_password = IAmPrincipal.generate_random_password()
# Encrypt the password
encryptor = Encryptor()
encrypted_password = encryptor.encrypt(random_password)
# save principal data
principal_obj = IAmPrincipal.objects.create(
email=form.cleaned_data.get('email'),
@@ -628,7 +634,7 @@ class CustomerCreateView(LoginRequiredMixin, generic.View):
last_name=form.cleaned_data.get('last_name'),
business_name=form.cleaned_data.get('business_name'),
phone_no=form.cleaned_data.get('phone_no'),
password=make_password("goodtimes#2024"),
password=make_password(random_password),
username=form.cleaned_data.get("email"),
email_verified=True,
register_complete=True,
@@ -651,6 +657,7 @@ class CustomerCreateView(LoginRequiredMixin, generic.View):
IAmPrincipalExtendedData.objects.create(
principal=principal_obj,
is_onboarded=True,
encrypted_pass=encrypted_password, # Save encrypted password
)
# save principal preferences record
@@ -665,7 +672,6 @@ class CustomerCreateView(LoginRequiredMixin, generic.View):
is_paid=True,
subscription=free_subscription
)
messages.success(self.request, constants.REGISTRATION_SUCCESS)
return redirect(self.success_url)
except Exception as e:
@@ -862,6 +868,27 @@ class CustomerListView(LoginRequiredMixin, generic.ListView):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
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
from openpyxl import Workbook, load_workbook
@@ -1022,7 +1049,7 @@ class CustomerTransferView(LoginRequiredMixin, generic.View):
# Send the email
try:
temp_password = "goodtimes#2024"
temp_password = "GoodTimes@2025"
principal_obj.password = make_password(temp_password)
principal_obj.save()
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.")
continue
random_password = IAmPrincipal.generate_random_password()
# Encrypt the password
encryptor = Encryptor()
encrypted_password = encryptor.encrypt(random_password)
# collect the principals
principal = IAmPrincipal(
first_name=first_name.strip().capitalize(),
last_name=last_name.strip().capitalize(),
email=email.strip(),
password=make_password("goodtimes#2024"),
password=make_password(random_password),
username=email.strip(),
email_verified=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)
# 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
subscription = PrincipalSubscription(

View File

@@ -726,9 +726,6 @@ class FacebookPoster:
self.facebook_api = facebook_api
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)
if not result:
return {'success': False, 'message': 'Error posting photo in Facebook'}
@@ -1130,4 +1127,18 @@ class StripeService:
)
return {'success': True, 'data': subscription}
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",
"django_quill",
"corsheaders",
'django_extensions',
"allauth",
"allauth.account",
"allauth.socialaccount",
@@ -211,6 +212,7 @@ TIME_FORMAT = "H:i p"
# otp expire time limit
OTP_EXPIRE_TIME = 1 # mins
DEFAULT_CHARSET = 'utf-8'
# Default primary key field type

View File

@@ -139,6 +139,7 @@ class CreateVenueApi(APIView):
def post(self, request):
data = request.data.copy()
print("prindata is ", data)
# Convert latitude and longitude to float and round to 8 decimal places
data["latitude"] = round(float(data["latitude"]), 8)

View File

@@ -148,6 +148,7 @@ class VenueForm(forms.ModelForm):
required=True
)
image = forms.ImageField(required=True)
postcode = forms.CharField(required=True, max_length=10)
latitude = forms.DecimalField(
widget=forms.NumberInput()
)
@@ -161,6 +162,7 @@ class VenueForm(forms.ModelForm):
"principal",
"title",
"address",
"postcode",
"image",
"latitude",
"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(
max_digits=14, decimal_places=8, blank=True, null=True
)
postcode = models.CharField(max_length=20, blank=True, null=True)
def __str__(self):
return self.title
class AgeGroups(BaseModel):
name = models.CharField(max_length=10, unique=True)
class Meta:
db_table = "age_group"

View File

@@ -599,12 +599,17 @@ class SocialMediaPostView(generic.View):
image_url = request.build_absolute_uri(event.image.url)
if platform in ['facebook', 'all']:
facebook_api = FacebookAPI()
facebook_poster = FacebookPoster(facebook_api)
result = facebook_poster.post_photo(image_url, caption)
if result["success"]:
success_messages.append("Posted to Facebook successfully")
else:
try:
print("facebook is called")
facebook_api = FacebookAPI()
facebook_poster = FacebookPoster(facebook_api)
result = facebook_poster.post_photo(image_url, caption)
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")
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
tweepy==4.14.0
Twisted==23.10.0
twisted-iocpsupport==1.0.4
# twisted-iocpsupport==1.0.4
txaio==23.1.1
typing_extensions==4.9.0
tzdata==2024.1

View File

@@ -1,8 +1,10 @@
{% extends 'layout/base_template.html' %}
{% load static %}
{% block stylesheet %}
<!-- include required css cdn link through html here -->
{% include "cdn_through_html/datatable_cdn_css.html" %}
<!-- include required css cdn link through html here -->
{% 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 %}
@@ -19,7 +21,7 @@
<a class="btn btn-dark mb-2 me-md-4" href="{% url 'accounts:download_excel_template' %}">
Download Excel Template
</a>
<a class="btn btn-dark mb-2 me-md-4" href="{% url 'accounts:import_customer_data' %}">
Import
</a>
@@ -27,12 +29,12 @@
<a class="btn btn-dark mb-2 me-md-4" href="{% url 'accounts:export_customer_data' %}">
Export
</a>
<a class="btn btn-primary mb-2 me-4" href="{% url 'accounts:customer_add' %}">Add Customer</a>
</div>
</div>
<div class="row layout-spacing">
<div class="col-lg-12">
<div class="statbox widget box box-shadow">
@@ -46,39 +48,39 @@
<th class="checkbox-column text-center sorting_asc" tabindex="0"
aria-controls="style-3" rowspan="1" colspan="1" aria-sort="ascending"
style="width: 69.2656px;"> Record Id </th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
style="width: 44.2344px;">Image</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" 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"
style="width: 77.3281px;">Last Name</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" 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"
style="width: 98.875px;">Mobile No.</th> -->
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 98.875px;">Principal Type</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
rowspan="1" style="width: 44.2344px;">Image</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
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" style="width: 77.3281px;">Last Name</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
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" style="width: 98.875px;">#</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
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"
style="width: 98.875px;">Phone Verified</th> -->
<th class="text-center sorting" tabindex="0" aria-controls="style-3" 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"
style="width: 98.875px;">Referral Count</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" 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"
style="width: 98.875px;">Transferred to Customer</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 98.875px;">Created On</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 98.875px;">Modified On</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" style="width: 98.875px;">Email Verified</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
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" style="width: 98.875px;">Onboarded by Admin</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
rowspan="1" colspan="1" style="width: 98.875px;">Transferred to Customer
</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
rowspan="1" colspan="1" style="width: 98.875px;">Created On</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3"
rowspan="1" colspan="1" style="width: 98.875px;">Modified On</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"
style="width: 98.875px;">Delete</th> -->
<th class="text-center dt-no-sorting" tabindex="0"
aria-controls="style-3" rowspan="1" colspan="1"
style="width: 51.625px;">Action</th>
<th class="text-center dt-no-sorting" tabindex="0" aria-controls="style-3"
rowspan="1" colspan="1" style="width: 51.625px;">Action</th>
</tr>
</thead>
<tbody>
@@ -92,33 +94,43 @@
<td>{{ data_obj.first_name }}</td>
<td>{{ data_obj.last_name }}</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.phone_verified }}</td> -->
<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 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 %}
{{ data_obj.extended_data.is_onboarded }}
{{ data_obj.extended_data.is_onboarded }}
{% else %}
False
False
{% endif %}
</span>
</td>
<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 %}
{{ data_obj.extended_data.is_transferred }}
{{ data_obj.extended_data.is_transferred }}
{% else %}
False
False
{% endif %}
</span>
</td>
<td>{{ data_obj.created_on }}</td>
<td>{{ data_obj.modified_on }}</td>
<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 }}
</span>
</td>
@@ -129,11 +141,12 @@
</td> -->
<td class="text-center">
<ul class="table-controls">
<li><a href="{% url 'accounts:customer_edit' data_obj.id%}" class="bs-tooltip"
data-bs-toggle="tooltip" data-bs-placement="top" title=""
data-original-title="Edit" data-bs-original-title="Edit"
aria-label="Edit"><svg xmlns="http://www.w3.org/2000/svg"
width="24" height="24" viewBox="0 0 24 24" fill="none"
<li><a href="{% url 'accounts:customer_edit' data_obj.id%}"
class="bs-tooltip" data-bs-toggle="tooltip"
data-bs-placement="top" title="" data-original-title="Edit"
data-bs-original-title="Edit" aria-label="Edit"><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-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">
</path>
</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">
<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>
<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">
<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>
</td>
</tr>
@@ -159,31 +183,88 @@
</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 %}
{% block javascript %}
<!-- include required js cdn link through html here -->
{% include "cdn_through_html/datatable_cdn_js.html" %}
<script>
c3 = $('#style-3').DataTable({
"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>>>" +
"<'table-responsive'tr>" +
"<'dt--bottom-section d-sm-flex justify-content-sm-between text-center'<'dt--pages-count mb-sm-0 mb-3'i><'dt--pagination'p>>",
"oLanguage": {
"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>' },
"sInfo": "Showing page _PAGE_ of _PAGES_",
"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>',
"sSearchPlaceholder": "Search...",
"sLengthMenu": "Results : _MENU_",
},
"order": [[ 0, "desc" ]],
"stripeClasses": [],
"lengthMenu": [5, 10, 20, 50],
"pageLength": 10
<!-- include required js cdn link through html here -->
{% include "cdn_through_html/datatable_cdn_js.html" %}
{% include "cdn_through_html/sweetalert2_cdn_js.html" %}
<script>
c3 = $('#style-3').DataTable({
"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>>>" +
"<'table-responsive'tr>" +
"<'dt--bottom-section d-sm-flex justify-content-sm-between text-center'<'dt--pages-count mb-sm-0 mb-3'i><'dt--pagination'p>>",
"oLanguage": {
"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>' },
"sInfo": "Showing page _PAGE_ of _PAGES_",
"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>',
"sSearchPlaceholder": "Search...",
"sLengthMenu": "Results : _MENU_",
},
"order": [[0, "desc"]],
"stripeClasses": [],
"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 %}

View File

@@ -45,6 +45,8 @@
aria-sort="ascending" style="width: 69.2656px;"> Title </th>
<th class="checkbox-column sorting_asc" tabindex="0" aria-controls="style-3"
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"
aria-sort="ascending" style="width: 69.2656px;"> Latitude </th>
<th class="checkbox-column sorting_asc" tabindex="0" aria-controls="style-3"
@@ -68,6 +70,7 @@
</td>
<td>{{data_obj.title}}</td>
<td>{{data_obj.address}}</td>
<td>{{data_obj.postcode}}</td>
<td>{{data_obj.latitude}}</td>
<td>{{data_obj.longitude}}</td>
<td class="text-center">