manage coupons list, add, edit, delete views

This commit is contained in:
rizwanisready
2024-07-22 21:08:56 +05:30
parent 473a961ab6
commit 6a8dc781d2
28 changed files with 672 additions and 926 deletions

View File

@@ -44,6 +44,7 @@ def compute_resource_action_constants(request):
'RESOURCE_MANAGE_NOTIFICATIONS': resource_action.RESOURCE_MANAGE_NOTIFICATIONS,
'RESOURCE_MANAGE_REFERRALS': resource_action.RESOURCE_MANAGE_REFERRALS,
'RESOURCE_MANAGE_FEEDBACK': resource_action.RESOURCE_MANAGE_FEEDBACK,
'RESOURCE_MANAGE_COUPONS': resource_action.RESOURCE_MANAGE_COUPONS,
'RESOURCE_IAM_PRINCIPAL': resource_action.RESOURCE_IAM_PRINCIPAL,
'RESOURCE_IAM_PRINCIPAL_GROUP': resource_action.RESOURCE_IAM_PRINCIPAL_GROUP,
'RESOURCE_IAM_GROUP': resource_action.RESOURCE_IAM_GROUP,

View File

@@ -27,7 +27,8 @@ from accounts.resource_action import (
RESOURCE_MANAGE_REFERRALS,
RESOURCE_MANAGE_FEEDBACK,
RESOURCE_MANAGE_WITHDRAWALS,
RESOURCE_MANAGE_BANK_ACCOUNTS
RESOURCE_MANAGE_BANK_ACCOUNTS,
RESOURCE_MANAGE_COUPONS
)
# this variable store the data of model principaltype, action, resource
fixture_data = [
@@ -334,4 +335,16 @@ fixture_data = [
"action": [1, 2, 3, 4],
},
},
{
"model": "accounts.iamappresource",
"pk": 18,
"fields": {
"name": RESOURCE_MANAGE_COUPONS,
"label": RESOURCE_MANAGE_COUPONS,
"slug": RESOURCE_MANAGE_COUPONS,
"created_on": "2023-09-28T16:17:42.815",
"modified_on": "2023-09-28T16:17:42.815",
"action": [1, 2, 3, 4],
},
},
]

View File

@@ -386,5 +386,22 @@
4
]
}
},
{
"model": "accounts.iamappresource",
"pk": 18,
"fields": {
"name": "manage_coupons",
"label": "manage_coupons",
"slug": "manage_coupons",
"created_on": "2023-09-28T16:17:42.815",
"modified_on": "2023-09-28T16:17:42.815",
"action": [
1,
2,
3,
4
]
}
}
]

View File

@@ -28,6 +28,7 @@ RESOURCE_MANAGE_REFERRALS = "manage_referrals"
RESOURCE_MANAGE_NOTIFICATIONS = "manage_notifications"
RESOURCE_MANAGE_WITHDRAWALS = "manage_withdrawals"
RESOURCE_MANAGE_BANK_ACCOUNTS = "manage_bank_accounts"
RESOURCE_MANAGE_COUPONS = "manage_coupons"
# These constants are used solely for managing the active and inactive state of pages

View File

@@ -53,5 +53,6 @@ STATICFILES_DIRS = [BASE_DIR.joinpath("static")]
STRIPE_CHECKOUT_URL = "http://localhost:8000/subscriptions/stripe-subscription/"
STRIPE_FINAL_URL = "http://localhost:8000/subscriptions/create-checkout-session/"
COUPON_VALIDITY_CHECK_URL = "http://localhost:8000/subscriptions/coupon-validity-check/"
LOGO_PATH = "static"

View File

@@ -82,5 +82,6 @@ STRIPE_CHECKOUT_URL = (
STRIPE_FINAL_URL = (
"https://admin.goodtimesltd.co.uk/subscriptions/create-checkout-session/"
)
COUPON_VALIDITY_CHECK_URL = "https://admin.goodtimesltd.co.uk/subscriptions/coupon-validity-check/"
LOGO_PATH = "/var/www/goodtimes_prod/goodtimes/static"

View File

@@ -82,5 +82,6 @@ STRIPE_CHECKOUT_URL = (
STRIPE_FINAL_URL = (
"https://staging.goodtimesltd.co.uk/subscriptions/create-checkout-session/"
)
COUPON_VALIDITY_CHECK_URL = "https://staging.goodtimesltd.co.uk/subscriptions/coupon-validity-check/"
LOGO_PATH = "/var/www/goodtimes/static"

View File

@@ -53,6 +53,9 @@ urlpatterns = [
path("subscriptions/", include("manage_subscriptions.urls")),
path("api/subscriptions/", include("manage_subscriptions.api.urls")),
path("coupons/", include("manage_coupons.urls")),
# path("api/coupons/", include("manage_coupons.api.urls")),
path("notifications/", include("manage_notifications.urls")),
path("api/notifications/", include("manage_notifications.api.urls")),

View File

@@ -5,6 +5,7 @@ from datetime import timedelta
from django.utils import timezone
from onesignal_sdk.client import Client as OneSignalClient
from accounts.models import IAmPrincipal
from manage_coupons.models import Coupon
from manage_notifications.models import (
IAmPrincipalNotificationSettings,
InAppNotification,
@@ -132,6 +133,16 @@ class WebhookService:
def get_order_id(self):
return self.charge_data["metadata"]["order_id"]
def get_coupon(self):
coupon_code = self.charge_data["metadata"].get("couponCode")
if coupon_code:
try:
return Coupon.objects.get(coupon_code=coupon_code)
except Coupon.DoesNotExist:
logger.error(f"Invalid coupon code: {coupon_code}")
raise ValueError(f"Invalid coupon code: {coupon_code}")
return None
class ReferralRewardService:
def __init__(self, principal, principal_subscription, subscription):
@@ -225,7 +236,7 @@ class SubscriptionService:
def __init__(self):
self.principal_subscription = None
def create_principal_subscription(self, principal, subscription, order_id):
def create_principal_subscription(self, principal, subscription, order_id, coupon=None):
subscription_days = subscription.plan.days
today = timezone.now().date()
last_date = today + timedelta(days=subscription_days)
@@ -237,7 +248,11 @@ class SubscriptionService:
start_date=today,
end_date=last_date,
grace_period_end_date=last_date + timedelta(days=15),
coupon_code=coupon.coupon_code if coupon else None,
)
if coupon:
coupon.no_of_redeems += 1
coupon.save()
self.principal_subscription = principal_subscription
return principal_subscription
@@ -260,6 +275,7 @@ class PaymentProcessingService:
self.transaction = self.webhook_service.get_transaction()
self.subscription = self.webhook_service.get_subscription()
self.order_id = self.webhook_service.get_order_id()
self.coupon = self.webhook_service.get_coupon()
self.subscription_service = SubscriptionService()
self.principal_subscription = None
@@ -274,7 +290,7 @@ class PaymentProcessingService:
# Create or update the principal subscription
self.principal_subscription = (
self.subscription_service.create_principal_subscription(
self.principal, self.subscription, self.order_id
self.principal, self.subscription, self.order_id, self.coupon
)
)
print("First Part Done....!!!!!")

View File

@@ -0,0 +1,47 @@
from django import forms
from django.core.exceptions import ValidationError
from manage_coupons.models import Coupon
class CouponForm(forms.ModelForm):
class Meta:
model = Coupon
fields = [
"title",
"coupon_code",
"description",
"image",
"discount_amount",
"discount_percentage",
"valid_from",
"valid_to",
"max_redeems",
]
widgets = {
"valid_from": forms.DateTimeInput(attrs={"type": "datetime-local"}),
"valid_to": forms.DateTimeInput(attrs={"type": "datetime-local"}),
}
def clean(self):
cleaned_data = super().clean()
discount_amount = cleaned_data.get("discount_amount")
discount_percentage = cleaned_data.get("discount_percentage")
valid_from = cleaned_data.get("valid_from")
valid_to = cleaned_data.get("valid_to")
if discount_amount and discount_percentage:
raise ValidationError(
"You can only set either a discount amount or a discount percentage, not both."
)
if not discount_amount and not discount_percentage:
raise ValidationError(
"You must set either a discount amount or a discount percentage."
)
if valid_from and valid_to and valid_from >= valid_to:
raise ValidationError(
"The valid_from date must be earlier than the valid_to date."
)
return cleaned_data

View File

@@ -0,0 +1,81 @@
# Generated by Django 5.0.2 on 2024-07-22 12:20
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="Coupon",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("active", models.BooleanField(default=True)),
("deleted", models.BooleanField(default=False)),
("created_on", models.DateTimeField(auto_now_add=True)),
("modified_on", models.DateTimeField(auto_now=True)),
("title", models.CharField(max_length=255)),
("coupon_code", models.CharField(max_length=50, unique=True)),
("no_of_redeems", models.IntegerField(default=0)),
("description", models.TextField(blank=True, null=True)),
(
"image",
models.ImageField(blank=True, null=True, upload_to="coupon_img"),
),
(
"discount_amount",
models.DecimalField(
blank=True, decimal_places=2, max_digits=10, null=True
),
),
(
"discount_percentage",
models.DecimalField(
blank=True, decimal_places=2, max_digits=5, null=True
),
),
("valid_from", models.DateTimeField()),
("valid_to", models.DateTimeField()),
("max_redeems", models.IntegerField(default=0)),
(
"created_by",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="%(class)s_created",
to=settings.AUTH_USER_MODEL,
),
),
(
"modified_by",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="%(class)s_modified",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"db_table": "coupon",
},
),
]

View File

@@ -2,12 +2,11 @@ from django.db import models
from django.utils import timezone
from accounts.models import BaseModel, IAmPrincipalType
# Create your models here.
class Coupon(BaseModel):
title = models.CharField(max_length=255)
coupon_code = models.CharField(max_length=50, unique=True)
no_of_redeems = models.IntegerField(default=0)
description = models.TextField(null=True, blank=True)
image = models.ImageField(upload_to="coupon_img", null=True, blank=True)
discount_amount = models.DecimalField(
@@ -16,11 +15,9 @@ class Coupon(BaseModel):
discount_percentage = models.DecimalField(
max_digits=5, decimal_places=2, null=True, blank=True
)
principal_types = models.ManyToManyField(
IAmPrincipalType, related_name="principal_type_coupons", blank=True
)
valid_from = models.DateTimeField()
valid_to = models.DateTimeField()
max_redeems = models.IntegerField(default=0)
class Meta:
db_table = "coupon"
@@ -28,6 +25,13 @@ class Coupon(BaseModel):
def __str__(self):
return self.coupon_code
# If max_redeems is 0, it means that we are allowing unlimited redeems
def is_valid(self):
now = timezone.now()
return self.active and not self.deleted and self.valid_from <= now <= self.valid_to
return (
self.active
and not self.deleted
and self.valid_from <= now <= self.valid_to
and (self.max_redeems == 0 or self.no_of_redeems < self.max_redeems)
)

View File

@@ -0,0 +1,23 @@
from django.urls import path
from . import views
app_name = "manage_coupons"
urlpatterns = [
path("coupon/list/", views.CouponView.as_view(), name="coupon_list"),
path(
"coupon/add/",
views.CouponCreateOrUpdateView.as_view(),
name="coupon_add",
),
path(
"coupon/edit/<int:pk>/",
views.CouponCreateOrUpdateView.as_view(),
name="coupon_edit",
),
path(
"coupon/delete/<int:pk>/",
views.CouponDeleteView.as_view(),
name="coupon_delete",
),
]

View File

@@ -1,3 +1,114 @@
from django.shortcuts import render
from django.shortcuts import get_object_or_404, render, redirect
from django.views import generic
from django.contrib.auth.mixins import LoginRequiredMixin
from accounts import resource_action
from django.urls import reverse_lazy
from django.contrib import messages
from goodtimes import constants
from manage_coupons.forms import CouponForm
from manage_coupons.models import Coupon
# Create your views here.
class CouponView(LoginRequiredMixin, generic.ListView):
page_name = resource_action.RESOURCE_MANAGE_COUPONS
resource = resource_action.RESOURCE_MANAGE_COUPONS
action = resource_action.ACTION_READ
model = Coupon
template_name = "manage_coupons/coupon_list.html"
context_object_name = "coupon_obj"
def get_queryset(self):
queryset = super().get_queryset().filter(deleted=False, active=True)
return queryset.order_by("-created_on")
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
return context
class CouponCreateOrUpdateView(LoginRequiredMixin, generic.View):
# Set the page_name and resource
page_name = resource_action.RESOURCE_MANAGE_COUPONS
resource = resource_action.RESOURCE_MANAGE_COUPONS
# Initialize the action as ACTION_CREATE (can change based on logic)
action = resource_action.ACTION_CREATE
template_name = "manage_coupons/coupon_add.html"
model = Coupon
form_class = CouponForm
success_url = reverse_lazy("manage_coupons:coupon_list")
error_message = "An error occurred while saving the data."
# Determine the success message dynamically based on whether it's an update or create
def get_success_message(self):
self.success_message = (
constants.RECORD_CREATED if not self.object else constants.RECORD_UPDATED
)
return self.success_message
# Get the object (if exists) based on URL parameter 'pk'
def get_object(self):
pk = self.kwargs.get("pk")
return get_object_or_404(self.model, pk=pk) if pk else None
# Add page_name and operation to the context
def get_context_data(self, **kwargs):
context = {
"page_name": self.page_name,
"operation": "Add" if not self.object else "Edit",
}
context.update(kwargs) # Include any additional context data passed to the view
return context
def get(self, request, *args, **kwargs):
self.object = self.get_object()
# If an object is found, change action to ACTION_UPDATE
if self.object is not None:
self.action = resource_action.ACTION_UPDATE
form = self.form_class(instance=self.object)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
# If an object is found, change action to ACTION_UPDATE
if self.object is not None:
self.action = resource_action.ACTION_UPDATE
form = self.form_class(request.POST, request.FILES, instance=self.object)
if not form.is_valid():
print(form.errors)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
form.save()
messages.success(self.request, self.get_success_message())
return redirect(self.success_url)
class CouponDeleteView(LoginRequiredMixin, generic.View):
page_name = resource_action.RESOURCE_MANAGE_COUPONS
resource = resource_action.RESOURCE_MANAGE_COUPONS
action = resource_action.ACTION_DELETE
model = Coupon
success_url = reverse_lazy("manage_coupons:coupon_list")
success_message = constants.RECORD_DELETED
error_message = constants.RECORD_NOT_FOUND
def get(self, request, pk):
try:
type_obj = self.model.objects.get(id=pk)
type_obj.deleted = True
type_obj.active = False
type_obj.save()
messages.success(request, self.success_message)
except self.model.DoesNotExist:
messages.success(request, self.error_message)
return redirect(self.success_url)

View File

@@ -1,4 +1,5 @@
from django import forms
from accounts.models import IAmPrincipalType
from manage_subscriptions.models import PrincipalSubscription, Subscription, Plan
@@ -31,7 +32,15 @@ class SubscriptionForm(forms.ModelForm):
"active",
"deleted",
"is_free",
] # Include all fields you want from the model
]
def __init__(self, *args, **kwargs):
super(SubscriptionForm, self).__init__(*args, **kwargs)
event_user = IAmPrincipalType.objects.get(name="event_user")
event_manager = IAmPrincipalType.objects.get(name="event_manager")
self.fields["principal_types"].queryset = IAmPrincipalType.objects.filter(
id__in=[event_user.id, event_manager.id]
)
class PrincipalSubscriptionForm(forms.ModelForm):

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0.2 on 2024-07-22 12:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("manage_subscriptions", "0008_subscription_is_free"),
]
operations = [
migrations.AddField(
model_name="principalsubscription",
name="coupon_code",
field=models.CharField(blank=True, max_length=255, null=True),
),
]

View File

@@ -71,6 +71,7 @@ class PrincipalSubscription(BaseModel):
payment_intent_client_secret = models.CharField(
max_length=255, null=True, blank=True
)
coupon_code = models.CharField(max_length=255, null=True, blank=True)
class Meta:
db_table = "principal_subscription"

View File

@@ -70,6 +70,11 @@ urlpatterns = [
views.create_checkout_session,
name="create_checkout_session",
),
path(
"coupon-validity-check/",
views.validate_coupon,
name="validate_coupon",
),
path("stripe/", views.SubscriptionPageView.as_view(), name="stripe"),
path("success/", views.SuccessView.as_view(), name="success"),
path("cancel/", views.CancelView.as_view(), name="cancel"),

View File

@@ -1,18 +1,16 @@
from datetime import timedelta
from decimal import Decimal
import json
from django.http import HttpResponseBadRequest, JsonResponse, HttpResponseRedirect
from django.http import HttpResponseBadRequest, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
import stripe
from accounts import resource_action
from accounts.models import IAmPrincipal
from goodtimes.utils import ApiResponse
from django.contrib.auth import authenticate, login
from django.contrib.auth import login
import jwt
from rest_framework_simplejwt.tokens import AccessToken
from django.utils import timezone
from django.contrib.auth import get_user_model
from manage_coupons.models import Coupon
from manage_subscriptions.forms import (
PlanForm,
SubscriptionForm,
PrincipalSubscriptionForm,
)
@@ -24,7 +22,6 @@ from manage_wallets.models import (
)
from .models import Plan, Subscription, PrincipalSubscription
from django.views import generic
from rest_framework import status
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.contrib import messages
@@ -32,12 +29,10 @@ from goodtimes import constants
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from django.conf import settings
from rest_framework.permissions import IsAuthenticated
from django.views.generic.base import TemplateView
from django.db.models import Q
# Create your views here.
from django.db.models import Q
class SubscriptionCreateOrUpdateView(LoginRequiredMixin, generic.View):
# Set the page_name and resource
@@ -101,7 +96,10 @@ class SubscriptionCreateOrUpdateView(LoginRequiredMixin, generic.View):
# This code ensures that only one free plan can be created by checking for existing free plans before saving a new one.
if form.cleaned_data.get("is_free"):
if self.model.objects.filter(Q(is_free=True) & Q(active=True)).exists():
messages.error(self.request, "A free plan is already available. Please deactivate the existing one before creating a new one.")
messages.error(
self.request,
"A free plan is already available. Please deactivate the existing one before creating a new one.",
)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
@@ -119,7 +117,12 @@ class SubscriptionView(LoginRequiredMixin, generic.ListView):
context_object_name = "subscription_obj"
def get_queryset(self):
queryset = super().get_queryset().filter(deleted=False, active=True).prefetch_related("principal_types")
queryset = (
super()
.get_queryset()
.filter(deleted=False, active=True)
.prefetch_related("principal_types")
)
return queryset.order_by("-created_on")
def get_context_data(self, **kwargs):
@@ -395,15 +398,19 @@ class SubscriptionPageView(TemplateView):
if request.user.is_authenticated:
print("request.user: ", request.user)
subscriptions = Subscription.objects.filter(
principal_types=request.user.principal_type, active=True, deleted=False, is_free=False
principal_types=request.user.principal_type,
active=True,
deleted=False,
is_free=False,
)
if subscriptions.exists():
context["subscriptions"] = subscriptions
context["stripeCheckoutUrl"] = settings.STRIPE_CHECKOUT_URL
context["stripeFinalUrl"] = settings.STRIPE_FINAL_URL
context["couponValidityCheckUrl"] = settings.COUPON_VALIDITY_CHECK_URL
else:
# Handle the case where no subscriptions are found for the principal type.
# Handling the case where no subscriptions are found for the principal type.
context["error"] = "No subscriptions found for your user type."
return context
@@ -420,18 +427,37 @@ def stripe_config(request):
def validate_coupon(request):
data = json.loads(request.body)
coupon_code = data.get("couponCode", None)
subscription_id = data.get("subscriptionId", None)
# Validate coupon code
try:
subscription = Subscription.objects.get(id=subscription_id)
except Subscription.DoesNotExist:
return JsonResponse({"error": "Subscription not found."}, status=404)
# Validating Coupon
if coupon_code:
try:
coupon = stripe.Coupon.retrieve(coupon_code)
if coupon["valid"] is False:
return JsonResponse({"error": "Invalid or expired coupon code."}, status=400)
return JsonResponse({"success": "Coupon code is valid."})
except stripe.error.InvalidRequestError:
return JsonResponse({"error": "Invalid coupon code."}, status=400)
else:
return JsonResponse({"error": "Coupon code not provided."}, status=400)
coupon = Coupon.objects.get(coupon_code=coupon_code)
if not coupon.is_valid():
return JsonResponse({"error": "Coupon is not valid."}, status=400)
if coupon.discount_amount and coupon.discount_amount > subscription.amount:
return JsonResponse(
{"error": "Coupon discount amount exceeds subscription amount."},
status=400,
)
if (
coupon.discount_percentage
and (coupon.discount_percentage / Decimal("100")) * subscription.amount
> subscription.amount
):
return JsonResponse(
{
"error": "Coupon discount percentage exceeds subscription amount."
},
status=400,
)
except Coupon.DoesNotExist:
return JsonResponse({"error": "Coupon not found."}, status=404)
@csrf_exempt
@@ -442,6 +468,7 @@ def create_checkout_session(request):
data = json.loads(request.body)
print("data: ", data)
subscription_id = data.get("subscriptionId", None)
coupon_code = data.get("couponCode", None)
principal_id = request.user.id
try:
@@ -453,7 +480,28 @@ def create_checkout_session(request):
"order_" + str(timezone.localtime().timestamp()) + str(request.user.email)
)
print("order_id: ", order_id)
# Calculating the final amount after applying the coupon discount
final_amount = subscription.amount
coupon = None
if coupon_code:
try:
coupon = Coupon.objects.get(coupon_code=coupon_code)
if coupon.is_valid():
if coupon.discount_amount:
final_amount -= coupon.discount_amount
elif coupon.discount_percentage:
final_amount -= final_amount * (
coupon.discount_percentage / Decimal("100")
)
final_amount = max(
0, final_amount
) # Ensuring the amount is not negative
else:
return JsonResponse(
{"error": "Invalid or expired coupon code."}, status=400
)
except Coupon.DoesNotExist:
return JsonResponse({"error": "Coupon not found."}, status=404)
# Create a Transaction object with status INITIATE
transaction = Transaction.objects.create(
principal=request.user,
@@ -461,7 +509,7 @@ def create_checkout_session(request):
transaction_type=TransactionType.PAYMENT, # or PAYMENT, as applicable
payment_method=PaymentMethod.CARD, # Assuming CARD for this example
transaction_status=TransactionStatus.INITIATE,
amount=subscription.amount, # Fetching amount from the Subscription object
amount=final_amount, # Fetching amount from the Subscription object
order_id=order_id,
comment="Principal Subscription Initiated",
)
@@ -492,7 +540,7 @@ def create_checkout_session(request):
"name": subscription.title, # Adjust with your subscription/product name
},
"unit_amount": int(
subscription.amount * 100
final_amount * 100
), # Unit amount in cents/pence
"tax_behavior": "inclusive", # or 'exclusive', based on your tax settings
},
@@ -509,6 +557,7 @@ def create_checkout_session(request):
"order_id": str(order_id),
"subscription_id": str(subscription.id),
"transaction_id": str(transaction.id),
"couponCode": str(coupon.coupon_code) if coupon else None,
# "principal_subscription_id": str(principal_subscription.id),
},
)
@@ -523,38 +572,3 @@ class SuccessView(TemplateView):
class CancelView(TemplateView):
template_name = "stripe_html/cancel.html"
# class IndexView(TemplateView):
# template_name = "stripe_html/index.html"
# def get(self, request, *args, **kwargs):
# # Example of extracting the token from a query parameter or cookie
# token = request.GET.get("token")
# # token = request.GET.get("token") or request.COOKIES.get("jwt")
# print("token: ", token)
# if token:
# try:
# # Decode and validate token
# payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
# print("payload: ", payload)
# try:
# UserModel = get_user_model()
# user = UserModel.objects.get(id=payload["user_id"])
# # Manually specify the authentication backend
# user.backend = "django.contrib.auth.backends.ModelBackend"
# # Log the user in
# login(request, user)
# print("Logged in user: ", user)
# except IAmPrincipal.DoesNotExist:
# # Handle expired token
# return HttpResponseBadRequest("No Principal Found")
# except jwt.ExpiredSignatureError:
# # Handle expired token
# return HttpResponseBadRequest("Expired Signature Error")
# except jwt.InvalidTokenError:
# return HttpResponseBadRequest("Invalid Token Error")
# return super().get(request, *args, **kwargs)

View File

@@ -154,6 +154,17 @@
</a>
</li>
{% endif %}
{% if user|has_resource_permission:resource_context.RESOURCE_MANAGE_COUPONS %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_COUPONS %}active{% endif %}">
<a href="{% url 'manage_coupons:coupon_list'%}" aria-expanded="false"
class="dropdown-toggle">
<div class="">
<span class="material-symbols-outlined">local_offer</span>
<span>Manage Coupons</span>
</div>
</a>
</li>
{% endif %}
{% if user|has_resource_permission:resource_context.RESOURCE_PRINCIPAL_SUBSCRIPTIONS %}
<li class="menu {% if page_name == resource_context.RESOURCE_PRINCIPAL_SUBSCRIPTIONS %}active{% endif %}">
<a href="{% url 'manage_subscriptions:principal_subscriptions_list'%}" aria-expanded="false"

View File

@@ -16,7 +16,7 @@
<div class="col-lg-12">
<div class="row mb-2">
<div class="col">
<h3>Add Stocks</h3>
<h3>{{operation}} {{page_name}}</h3>
</div>
<div class="col text-end">
<button class="btn btn-dark mb-2 me-4" onclick="history.back()">
@@ -30,7 +30,7 @@
<div class="statbox widget box box-shadow">
<div class="widget-content widget-content-area">
<form method="POST">
<form method="POST" novalidate>
{% csrf_token %}
{% include 'includes/dynamic_template_form.html' with form=form %}
<div class="mt-4 mb-0">
@@ -67,69 +67,3 @@
{% endblock content %}
{% block javascript %}
<!-- include required css cdn link through html here -->
{% include "cdn_through_html/filepond_cdn_js.html" %}
{% include "cdn_through_html/quill_cdn_js.html" %}
{% include "cdn_through_html/tagify_cdn_js.html" %}
<script>
/**
* ===================================
* Blog Description Editor
* ===================================
*/
var quill = new Quill('#description', {
modules: {
toolbar: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
['image', 'code-block']
]
},
placeholder: 'Write description...',
theme: 'snow' // or 'bubble'
});
/**
* ====================
* File Pond
* ====================
*/
// We want to preview images, so we register
// the Image Preview plugin, We also register
// exif orientation (to correct mobile image
// orientation) and size validation, to prevent
// large files from being added
FilePond.registerPlugin(
FilePondPluginImagePreview,
FilePondPluginImageExifOrientation,
FilePondPluginFileValidateSize,
// FilePondPluginImageEdit
);
// Select the file input and use
// create() to turn it into a pond
var ecommerce = FilePond.create(document.querySelector('.file-upload-multiple'));
/**
* =====================
* Blog Tags
* =====================
*/
// The DOM element you wish to replace with Tagify
var input = document.querySelector('#id_tags');
// initialize Tagify on the above input node reference
new Tagify(input,{
originalInputValueFormat: valuesArr => valuesArr.map(item => item.value).join(', ')
})
</script>
{% endblock %}

View File

@@ -0,0 +1,163 @@
{% 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" %}
{% endblock %}
{% block content %}
<div class="row layout-top-spacing">
<div class="col-lg-12">
<div class="row">
<div class="col-sm-6">
<h3>Manage Coupons</h3>
</div>
<div class="col-sm-6 text-md-end">
<!--
<button class="btn btn-dark mb-2 me-md-4" onclick="history.back()">
<i class="fa fa-arrow-left"></i>
Back
</button>
-->
<a class="btn btn-primary mb-2" href="{% url 'manage_coupons:coupon_add' %}">Add Coupons</a>
</div>
</div>
<div class="row layout-spacing">
<div class="col-lg-12">
<div class="statbox widget box box-shadow">
<div class="widget-content widget-content-area">
<div id="style-3_wrapper" class="dataTables_wrapper container-fluid dt-bootstrap4 no-footer">
<div class="table-responsive">
<table id="style-3" class="table style-3 dt-table-hover dataTable no-footer" role="grid"
aria-describedby="style-3_info">
<thead>
<tr role="row">
<th class="checkbox-column sorting_asc" tabindex="0"
aria-controls="style-3" aria-sort="ascending"
style="width: 69.2656px;"> Record Id </th>
<th class="checkbox-column sorting_asc" tabindex="0"
aria-controls="style-3" 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;"> Code </th>
<th class="checkbox-column sorting_asc" tabindex="0"
aria-controls="style-3" aria-sort="ascending"
style="width: 69.2656px;"> Used </th>
<th class="checkbox-column sorting_asc" tabindex="0"
aria-controls="style-3" aria-sort="ascending"
style="width: 69.2656px;"> Max Allowed </th>
<th class="checkbox-column sorting_asc" tabindex="0"
aria-controls="style-3" aria-sort="ascending"
style="width: 69.2656px;"> From </th>
<th class="checkbox-column sorting_asc" tabindex="0"
aria-controls="style-3" aria-sort="ascending"
style="width: 69.2656px;"> To </th>
<th class="checkbox-column sorting_asc" tabindex="0"
aria-controls="style-3" aria-sort="ascending"
style="width: 69.2656px;"> Amount </th>
<th class="checkbox-column sorting_asc" tabindex="0"
aria-controls="style-3" aria-sort="ascending"
style="width: 69.2656px;"> Percentage </th>
<th class="sorting" tabindex="7" aria-controls="style-3"
style="width: 79.7969px;">Active</th>
<th class="dt-no-sorting sorting" tabindex="8"
aria-controls="style-3"
style="width: 100.625px;">Action</th>
</tr>
</thead>
<tbody>
{% for data_obj in coupon_obj %}
<tr role="row">
<td class="checkbox-column text-center sorting_1"> {{data_obj.id}}</td>
<td>{{data_obj.title}}</td>
<td>{{data_obj.coupon_code}}</td>
<td>{{data_obj.no_of_redeems}}</td>
<td>{{data_obj.max_redeems}}</td>
<td>{{data_obj.valid_from}}</td>
<td>{{data_obj.valid_to}}</td>
<td>{{data_obj.discount_amount}}</td>
<td>{{data_obj.discount_percentage}}</td>
<td class="text-center">
<span class="shadow-none badge {% if data_obj.active %}badge-primary{% else %}badge-danger{% endif %}">{{data_obj.active}}</span>
</td>
<td class="text-center">
<ul class="table-controls">
<li><a href="{% url 'manage_coupons:coupon_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">
<path
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 'manage_coupons:coupon_delete' data_obj.id %}" class="bs-tooltip"
data-bs-toggle="tooltip" data-bs-placement="top" title=""
data-original-title="Delete" data-bs-original-title="Delete"
aria-label="Delete"><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-trash p-1 br-8 mb-1">
<polyline points="3 6 5 6 21 6"></polyline>
<path
d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2">
</path>
</svg>
</a>
</li>
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</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
});
multiCheck(c3);
</script>
{% endblock %}

View File

@@ -1,135 +0,0 @@
{% extends 'layout/base_template.html' %}
{% load static %}
{% block stylesheet %}
<!-- include required css cdn link through html here -->
{% include "cdn_through_html/filepond_cdn_css.html" %}
{% include "cdn_through_html/quill_cdn_css.html" %}
{% include "cdn_through_html/tagify_cdn_css.html" %}
{{form.media}}
{% endblock %}
{% block content %}
<div class="row layout-top-spacing">
<div class="col-lg-12">
<div class="row mb-2">
<div class="col">
<h3>Add Stock Index</h3>
</div>
<div class="col text-end">
<button class="btn btn-dark mb-2 me-4" onclick="history.back()">
<i class="fa fa-arrow-left"></i>
Back
</button>
</div>
</div>
<div class="row layout-spacing">
<div class="col-lg-12">
<div class="statbox widget box box-shadow">
<div class="widget-content widget-content-area">
<form method="POST">
{% csrf_token %}
{% include 'includes/dynamic_template_form.html' with form=form %}
<div class="mt-4 mb-0">
<div class="d-grid"><button class="btn btn-primary btn-block" type="submit">Submit</button></div>
</div>
{% comment %} <div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" id="title" aria-describedby="title">
<div id="emailHelp" class="form-text" style="color: grey;">We'll never share your email with anyone else.</div>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<div id="description"></div>
</div>
<div class="mb-3">
<label for="product-images">Image</label>
<div class="multiple-file-upload">
<div class="filepond--root filepond file-upload-multiple filepond--hopper" id="images" data-style-button-remove-item-position="left" data-style-button-process-item-position="right" data-style-load-indicator-position="right" data-style-progress-indicator-position="right" data-style-button-remove-item-align="false" style="height: 57px;"><input class="filepond--browser" type="file" id="filepond--browser-feeq8o6dj" name="filepond" aria-controls="filepond--assistant-feeq8o6dj" aria-labelledby="filepond--drop-label-feeq8o6dj" multiple=""><a class="filepond--credits" aria-hidden="true" href="https://pqina.nl/" target="_blank" rel="noopener noreferrer" style="transform: translateY(49px);">Powered by PQINA</a><div class="filepond--drop-label" style="transform: translate3d(0px, 0px, 0px); opacity: 1;"><label for="filepond--browser-feeq8o6dj" id="filepond--drop-label-feeq8o6dj" aria-hidden="true">Drag &amp; Drop your files or <span class="filepond--label-action" tabindex="0">Browse</span></label></div><div class="filepond--list-scroller" style="transform: translate3d(0px, 41px, 0px);"><ul class="filepond--list" role="list"></ul></div><div class="filepond--panel filepond--panel-root" data-scalable="true"><div class="filepond--panel-top filepond--panel-root"></div><div class="filepond--panel-center filepond--panel-root" style="transform: translate3d(0px, 8px, 0px) scale3d(1, 0.41, 1);"></div><div class="filepond--panel-bottom filepond--panel-root" style="transform: translate3d(0px, 49px, 0px);"></div></div><span class="filepond--assistant" id="filepond--assistant-feeq8o6dj" role="status" aria-live="polite" aria-relevant="additions"></span><div class="filepond--drip"></div><fieldset class="filepond--data"></fieldset></div>
</div>
</div>
<div class="mb-3">
<label for="tags">Tags</label>
<input id="tags" class="tags" value="">
</div>
<button type="submit" class="btn btn-primary">Submit</button> {% endcomment %}
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
{% block javascript %}
<!-- include required css cdn link through html here -->
{% include "cdn_through_html/filepond_cdn_js.html" %}
{% include "cdn_through_html/quill_cdn_js.html" %}
{% include "cdn_through_html/tagify_cdn_js.html" %}
<script>
/**
* ===================================
* Blog Description Editor
* ===================================
*/
var quill = new Quill('#description', {
modules: {
toolbar: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
['image', 'code-block']
]
},
placeholder: 'Write description...',
theme: 'snow' // or 'bubble'
});
/**
* ====================
* File Pond
* ====================
*/
// We want to preview images, so we register
// the Image Preview plugin, We also register
// exif orientation (to correct mobile image
// orientation) and size validation, to prevent
// large files from being added
FilePond.registerPlugin(
FilePondPluginImagePreview,
FilePondPluginImageExifOrientation,
FilePondPluginFileValidateSize,
// FilePondPluginImageEdit
);
// Select the file input and use
// create() to turn it into a pond
var ecommerce = FilePond.create(document.querySelector('.file-upload-multiple'));
/**
* =====================
* Blog Tags
* =====================
*/
// The DOM element you wish to replace with Tagify
var input = document.querySelector('#id_tags');
// initialize Tagify on the above input node reference
new Tagify(input,{
originalInputValueFormat: valuesArr => valuesArr.map(item => item.value).join(', ')
})
</script>
{% endblock %}

View File

@@ -1,237 +0,0 @@
{% 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 "cdn_through_html/tabs_cdn_css.html" %}
{% endblock %}
{% block content %}
<div class="row layout-top-spacing">
<div class="col-lg-12">
<div class="row mb-2">
<div class="col">
<h3>Manage Stock</h3>
</div>
<div class="col text-end">
{% comment %} <button class="btn btn-dark mb-2 me-4" onclick="history.back()">
<i class="fa fa-arrow-left"></i>
Back
</button> {% endcomment %}
<a class="btn btn-success mb-2 me-4" href="{% url 'manage_stock:stock_index_add' %}">Add Stock Index</a>
<a class="btn btn-primary mb-2 me-4" href="{% url 'manage_stock:stock_add' %}">Add Stock</a>
</div>
</div>
<div class="row">
<div id="tabsSimple" class="col-xl-12 col-12 layout-spacing">
<div class="statbox widget box box-shadow">
<div class="widget-content widget-content-area">
<div class="simple-pill">
<ul class="nav nav-pills mb-3" id="pills-tab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link" id="pills-category-tab" data-bs-toggle="pill" data-bs-target="#pills-category" type="button" role="tab" aria-controls="pills-category" aria-selected="false">Stock Index List</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link active" id="pills-new-article-tab" data-bs-toggle="pill" data-bs-target="#pills-new-article" type="button" role="tab" aria-controls="pills-new-article" aria-selected="true">Stock List</button>
</li>
</ul>
<div class="tab-content" id="pills-tabContent">
<div class="tab-pane fade" id="pills-category" role="tabpanel" aria-labelledby="pills-category-tab" tabindex="0">
<div id="category_wrapper" class="dataTables_wrapper container-fluid dt-bootstrap4 no-footer">
<div class="table-responsive">
<table id="category" class="table style-3 dt-table-hover dataTable no-footer" role="grid"
aria-describedby="style-3_info">
<thead>
<tr role="row">
<th class="checkbox-column sorting_asc" tabindex="0"
aria-controls="style-3" aria-sort="ascending"
style="width: 69.2656px;"> Record Id </th>
<th class="sorting" tabindex="0" aria-controls="style-3"
colspan="1"
style="width: 44.2344px;">Category Name</th>
<th class="sorting" tabindex="0" aria-controls="style-3"
style="width: 79.7969px;">Active</th>
<th class="dt-no-sorting sorting" tabindex="0"
aria-controls="style-3"
style="width: 51.625px;">Action</th>
</tr>
</thead>
<tbody>
{% for category in stock_index_obj %}
<tr role="row">
<td class="checkbox-column text-center sorting_1"> {{category.id}}</td>
<td>{{category.title}}</td>
<td class="text-center">
<span class="shadow-none badge {% if category.active %}badge-primary{% else %}badge-danger{% endif %}">{{category.active}}</span>
</td>
<td class="text-center">
<ul class="table-controls">
<li><a href="{% url 'manage_stock:stock_index_edit' category.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">
<path
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 'manage_stock:stock_index_delete' category.id %}" class="bs-tooltip"
data-bs-toggle="tooltip" data-bs-placement="top" title=""
data-original-title="Delete" data-bs-original-title="Delete"
aria-label="Delete"><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-trash p-1 br-8 mb-1">
<polyline points="3 6 5 6 21 6"></polyline>
<path
d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2">
</path>
</svg></a></li>
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="tab-pane fade active show" id="pills-new-article" role="tabpanel" aria-labelledby="pills-new-article-tab" tabindex="0">
<div id="new-article_wrapper" class="dataTables_wrapper container-fluid dt-bootstrap4 no-footer">
<div class="table-responsive">
<table id="new-article" class="table style-3 dt-table-hover dataTable no-footer" role="grid"
aria-describedby="style-3_info">
<thead>
<tr role="row">
<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"
colspan="1"
style="width: 44.2344px;">Category</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 79.7969px;">Title</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 79.7969px;">Active</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 51.625px;">Action</th>
</tr>
</thead>
<tbody>
{% for data_obj in stock_obj %}
<tr role="row">
<td class="checkbox-column text-center sorting_1">{{data_obj.id}}</td>
<td>{{data_obj.index_type.title}}</td>
<td>{{data_obj.title}}</td>
<td class="text-center">
<span class="shadow-none badge {% if data_obj.active %}badge-primary{% else %}badge-danger{% endif %}">{{data_obj.active}}</span>
</td>
<td class="text-center">
<ul class="table-controls">
<li><a href="{% url 'manage_stock:stock_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">
<path
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 'manage_stock:stock_delete' data_obj.id %}" class="bs-tooltip"
data-bs-toggle="tooltip" data-bs-placement="top" title=""
data-original-title="Delete" data-bs-original-title="Delete"
aria-label="Delete"><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-trash p-1 br-8 mb-1">
<polyline points="3 6 5 6 21 6"></polyline>
<path
d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2">
</path>
</svg></a></li>
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</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>
category = $('#category').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_",
},
"stripeClasses": [],
"lengthMenu": [5, 10, 20, 50],
"pageLength": 10
});
new_article = $('#new-article').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_",
},
"stripeClasses": [],
"lengthMenu": [5, 10, 20, 50],
"pageLength": 10
});
</script>
{% endblock %}

View File

@@ -1,175 +0,0 @@
{% 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 "cdn_through_html/tabs_cdn_css.html" %}
{% endblock %}
{% block content %}
<div class="row layout-top-spacing">
<div class="col-lg-12">
<div class="row mb-2">
<div class="col">
<h3>Manage Stock</h3>
</div>
<div class="col text-end">
{% comment %} <button class="btn btn-dark mb-2 me-4" onclick="history.back()">
<i class="fa fa-arrow-left"></i>
Back
</button> {% endcomment %}
<a class="btn btn-success mb-2 me-4" href="{% url 'manage_stock:stock_index_add' %}">Add Stock Index</a>
<a class="btn btn-primary mb-2 me-4" href="{% url 'manage_stock:stock_add' %}">Add Stock</a>
</div>
</div>
<div class="row">
<div id="tabsSimple" class="col-xl-12 col-12 layout-spacing">
<div class="statbox widget box box-shadow">
<div class="widget-content widget-content-area">
<div class="simple-pill">
<ul class="nav nav-pills mb-3" id="pills-tab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link" id="pills-category-tab" data-bs-toggle="pill" data-bs-target="#pills-category" type="button" role="tab" aria-controls="pills-category" aria-selected="false">Stock Index List</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link active" id="pills-new-article-tab" data-bs-toggle="pill" data-bs-target="#pills-new-article" type="button" role="tab" aria-controls="pills-new-article" aria-selected="true">Stock List</button>
</li>
</ul>
<div class="tab-content" id="pills-tabContent">
<div class="tab-pane fade" id="pills-category" role="tabpanel" aria-labelledby="pills-category-tab" tabindex="0">
<div id="category_wrapper" class="dataTables_wrapper container-fluid dt-bootstrap4 no-footer">
<div class="table-responsive">
<table id="category" class="table style-3 dt-table-hover dataTable no-footer" role="grid"
aria-describedby="style-3_info">
<thead>
<tr role="row">
<th class="checkbox-column sorting_asc" tabindex="0"
aria-controls="style-3" aria-sort="ascending"
style="width: 69.2656px;"> Record Id </th>
<th class="sorting" tabindex="0" aria-controls="style-3"
colspan="1"
style="width: 44.2344px;">Category Name</th>
<th class="sorting" tabindex="0" aria-controls="style-3"
style="width: 79.7969px;">Active</th>
<th class="dt-no-sorting sorting" tabindex="0"
aria-controls="style-3"
style="width: 51.625px;">Action</th>
</tr>
</thead>
<tbody>
{% for category in stock_price_obj %}
<tr role="row">
<td class="checkbox-column text-center sorting_1"> {{category.id}}</td>
<td>{{category.title}}</td>
<td class="text-center">
<span class="shadow-none badge {% if category.active %}badge-primary{% else %}badge-danger{% endif %}">{{category.active}}</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="tab-pane fade active show" id="pills-new-article" role="tabpanel" aria-labelledby="pills-new-article-tab" tabindex="0">
<div id="new-article_wrapper" class="dataTables_wrapper container-fluid dt-bootstrap4 no-footer">
<div class="table-responsive">
<table id="new-article" class="table style-3 dt-table-hover dataTable no-footer" role="grid"
aria-describedby="style-3_info">
<thead>
<tr role="row">
<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"
colspan="1"
style="width: 44.2344px;">Category</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 79.7969px;">Title</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 51.625px;">Action</th>
</tr>
</thead>
<tbody>
{% for data_obj in stock_price_obj %}
<tr role="row">
<td class="checkbox-column text-center sorting_1">{{data_obj.id}}</td>
<td>{{data_obj.index_type.title}}</td>
<td>{{data_obj.title}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</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>
category = $('#category').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_",
},
"stripeClasses": [],
"lengthMenu": [5, 10, 20, 50],
"pageLength": 10
});
new_article = $('#new-article').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_",
},
"stripeClasses": [],
"lengthMenu": [5, 10, 20, 50],
"pageLength": 10
});
</script>
{% endblock %}

View File

@@ -1,156 +0,0 @@
{% 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" %}
{% endblock %}
{% block content %}
<div class="row layout-top-spacing">
<div class="col-lg-12">
<div class="row">
<div class="col-sm-6">
<h3>{{data_objs.0.team.title}}</h3>
</div>
<div class="col-sm-6 text-md-end">
<!--
<button class="btn btn-dark mb-2 me-md-4" onclick="history.back()">
<i class="fa fa-arrow-left"></i>
Back
</button>
-->
{% comment %} <a class="btn btn-primary mb-2 me-md-4" href="{% url 'manage_games:game_entryfee_add' %}">Example form</a> {% endcomment %}
{% comment %} <a class="btn btn-primary mb-2" href="{% url 'manage_games:game_entryfee_add' %}">Add Entry Fee</a> {% endcomment %}
</div>
</div>
<div class="row layout-spacing">
<div class="col-lg-12">
<div class="statbox widget box box-shadow">
<div class="widget-content widget-content-area">
<div id="style-3_wrapper" class="dataTables_wrapper container-fluid dt-bootstrap4 no-footer">
<div class="table-responsive">
<table id="style-3" class="table style-3 dt-table-hover dataTable no-footer" role="grid"
aria-describedby="style-3_info">
<thead>
<tr role="row">
<!-- <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;">Team</th> -->
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
style="width: 44.2344px;">Stock</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
style="width: 44.2344px;">Quantity</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
style="width: 44.2344px;">Operation</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
style="width: 44.2344px;">Position</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
style="width: 44.2344px;">Last Closing</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
style="width: 44.2344px;">Current Closing</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
style="width: 44.2344px;">Current Closing Percentage</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
style="width: 44.2344px;">Stock Score</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 dt-no-sorting" tabindex="0"
aria-controls="style-3" rowspan="1" colspan="1"
style="width: 51.625px;">Action</th> -->
</tr>
</thead>
<tbody>
{% for data_obj in data_objs %}
<tr role="row">
<!-- <td class="checkbox-column text-center sorting_1"> {{ data_obj.id }} </td>
<td>{{ data_obj.team }}</td> -->
<td>{{ data_obj.stock }}</td>
<td>{{ data_obj.quantity }}</td>
<td>{{ data_obj.get_operation_display }}</td>
<td>{{ data_obj.get_position_display }}</td>
<td>{{ data_obj.last_closing }}</td>
<td>{{ data_obj.current_closing }}</td>
<td>{{ data_obj.current_closing_percentage }}</td>
<td>{{ data_obj.stock_score }}</td>
<td class="text-center">
<span class="shadow-none badge {% if data_obj.active %}badge-primary{% else %}badge-danger{% endif %}">
{{ data_obj.active }}
</span>
</td>
<!-- <td class="text-center">
<ul class="table-controls">
<li><a href="{% url 'manage_games:game_entryfee_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">
<path
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="javascript:void(0);" class="bs-tooltip"
data-bs-toggle="tooltip" data-bs-placement="top" title=""
data-original-title="Delete" data-bs-original-title="Delete"
aria-label="Delete"><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-trash p-1 br-8 mb-1">
<polyline points="3 6 5 6 21 6"></polyline>
<path
d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2">
</path>
</svg></a></li>
</ul>
</td> -->
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</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_",
},
"stripeClasses": [],
"lengthMenu": [5, 10, 20, 50],
"pageLength": 20
});
multiCheck(c3);
</script>
{% endblock %}

View File

@@ -67,69 +67,3 @@
{% endblock content %}
{% block javascript %}
<!-- include required css cdn link through html here -->
{% include "cdn_through_html/filepond_cdn_js.html" %}
{% include "cdn_through_html/quill_cdn_js.html" %}
{% include "cdn_through_html/tagify_cdn_js.html" %}
<script>
/**
* ===================================
* Blog Description Editor
* ===================================
*/
var quill = new Quill('#description', {
modules: {
toolbar: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
['image', 'code-block']
]
},
placeholder: 'Write description...',
theme: 'snow' // or 'bubble'
});
/**
* ====================
* File Pond
* ====================
*/
// We want to preview images, so we register
// the Image Preview plugin, We also register
// exif orientation (to correct mobile image
// orientation) and size validation, to prevent
// large files from being added
FilePond.registerPlugin(
FilePondPluginImagePreview,
FilePondPluginImageExifOrientation,
FilePondPluginFileValidateSize,
// FilePondPluginImageEdit
);
// Select the file input and use
// create() to turn it into a pond
var ecommerce = FilePond.create(document.querySelector('.file-upload-multiple'));
/**
* =====================
* Blog Tags
* =====================
*/
// The DOM element you wish to replace with Tagify
var input = document.querySelector('#id_tags');
// initialize Tagify on the above input node reference
new Tagify(input,{
originalInputValueFormat: valuesArr => valuesArr.map(item => item.value).join(', ')
})
</script>
{% endblock %}

View File

@@ -130,8 +130,9 @@
<div class="Adventure-btn text-center">
<input type="text" placeholder="Enter Coupon Code" class="form-control coupon-code-input" size="20">
<!-- Add a data attribute to store subscription ID -->
<button class="common-btn subscribe-btn" data-subscription-id="{{ subscription.id }}">Join
now</button>
<button class="common-btn subscribe-btn" data-subscription-id="{{ subscription.id }}">Join now</button>
<!-- Error message container -->
<div class="alert alert-danger coupon-error-message mt-2" style="display: none;"></div>
</div>
</div>
@@ -518,38 +519,77 @@
console.log("Sanity check!");
var stripeCheckoutUrl = "{{ stripeCheckoutUrl }}";
var stripeFinalUrl = "{{ stripeFinalUrl }}";
var couponValidityCheckUrl = "{{ couponValidityCheckUrl }}";
console.log("stripeCheckoutUrl: ", stripeCheckoutUrl);
console.log("stripeFinalUrl: ", stripeFinalUrl);
// Get Stripe publishable key
console.log("couponValidityCheckUrl: ", couponValidityCheckUrl);
// Geting Stripe publishable key
fetch(stripeCheckoutUrl)
.then((result) => { return result.json(); })
.then((result) => {
return result.json();
})
.then((data) => {
// Initialize Stripe.js
// Initializing Stripe.js -- getting stripe public key to generate stripe object for creating checkout session
const stripe = Stripe(data.publicKey);
console.log("loaded stripe public key");
document.querySelectorAll(".subscribe-btn").forEach(button => {
button.addEventListener("click", () => {
const subscriptionId = button.getAttribute("data-subscription-id");
const couponCode = button.previousElementSibling.value;
const errorMessageContainer = button.nextElementSibling;
console.log("subscriptionId: ", subscriptionId);
console.log("couponCode: ", couponCode);
button.disabled = true;
button.previousElementSibling.value = "";
// Create checkout session for the selected subscription
fetch(stripeFinalUrl, {
method: "POST",
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ subscriptionId: subscriptionId }),
})
.then((result) => { return result.json(); })
.then((data) => {
// Redirect to Stripe Checkout
return stripe.redirectToCheckout({ sessionId: data.sessionId })
// Handling any coupon validation errors here before creating stripe final checkout session
fetch(couponValidityCheckUrl, {
method: "POST",
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
subscriptionId: subscriptionId,
couponCode: couponCode
}),
})
.then(response => {
if (!response.ok) {
return response.json().then(data => {
throw new Error(data.error);
});
}
return response.json();
})
.then(data => {
// Creating checkout session for the selected subscription
fetch(stripeFinalUrl, {
method: "POST",
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
subscriptionId: subscriptionId
}),
})
.then((result) => {
return result.json();
})
.then((data) => {
// Redirects to Stripe Checkout
return stripe.redirectToCheckout({
sessionId: data.sessionId
})
})
.catch((error) => {
console.error("Error:", error);
button.disabled = false;
});
})
.catch((error) => {
console.error("Error:", error);
errorMessageContainer.style.display = 'block';
errorMessageContainer.innerText = error.message;
button.disabled = false;
});
});