notifications
@@ -149,6 +149,7 @@ class ProfileSerializer(serializers.ModelSerializer):
|
||||
"principal_type",
|
||||
"principal_type_name",
|
||||
"profile_photo",
|
||||
"player_id",
|
||||
"first_name",
|
||||
"last_name",
|
||||
"email",
|
||||
@@ -284,3 +285,7 @@ class ProfilePhotoSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = IAmPrincipal
|
||||
fields = ("email", "profile_photo", "first_name")
|
||||
|
||||
|
||||
class PlayerIDSerializer(serializers.Serializer):
|
||||
player_id = serializers.CharField(max_length=255)
|
||||
|
||||
@@ -23,8 +23,16 @@ urlpatterns = [
|
||||
path('profile/password-reset/', views.ProfilePasswordResetView.as_view(), name='password_reset'),
|
||||
# path('profile/kyc/', views.KycDocumentView.as_view(), name='pofile_kyc'),
|
||||
|
||||
path('player-id/add/', views.IAmPrincipalPlayerIDAPIView.as_view(), name='add_player_id'),
|
||||
|
||||
path('google-login/', views.GoogleLoginAPIView.as_view(), name='google-login'),
|
||||
|
||||
path(
|
||||
"apple-login/",
|
||||
views.decode_apple_token,
|
||||
name="apple_login",
|
||||
),
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import json
|
||||
from django.db import transaction
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils import timezone
|
||||
import jwt
|
||||
from rest_framework import status
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from django.conf import settings
|
||||
import requests
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.decorators.http import require_http_methods
|
||||
|
||||
|
||||
# from .authenticate import authticate_with_otp_and_passsword
|
||||
from accounts.models import (
|
||||
@@ -32,6 +38,7 @@ from .serializers import (
|
||||
# RegistrationPasswordSerializer,
|
||||
# PhoneSerializer,
|
||||
EmailSerializer,
|
||||
PlayerIDSerializer,
|
||||
RegistrationPasswordSerializer,
|
||||
RegistrationSerializer,
|
||||
ReferralCodeSerializer,
|
||||
@@ -156,6 +163,7 @@ class RegistrationDetailsView(APIView):
|
||||
email = request.data.get("email")
|
||||
print("email: ", email)
|
||||
referral_code = request.data.get("referral_code")
|
||||
player_id = request.data.get("player_id")
|
||||
print("referral_code", referral_code)
|
||||
|
||||
# Filter the user instance by phone number through reusable function
|
||||
@@ -177,6 +185,8 @@ class RegistrationDetailsView(APIView):
|
||||
try:
|
||||
instance = serializer.save()
|
||||
instance.register_complete = True
|
||||
if player_id:
|
||||
instance.player_id = player_id
|
||||
instance.save()
|
||||
except Exception as e:
|
||||
print("instance: E-", e)
|
||||
@@ -499,7 +509,9 @@ class ProfileView(APIView):
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
serializer = self.serializer(instance, data=request.data)
|
||||
serializer = self.serializer(
|
||||
instance, data=request.data, context={"request": request}
|
||||
)
|
||||
if not serializer.is_valid():
|
||||
error_response = {
|
||||
"status": status.HTTP_400_BAD_REQUEST,
|
||||
@@ -514,7 +526,7 @@ class ProfileView(APIView):
|
||||
return ApiResponse.error(
|
||||
message=constants.INTERNAL_SERVER_ERROR, errors=str(e)
|
||||
)
|
||||
return ApiResponse.success(message=constants.SUCCESS)
|
||||
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
|
||||
|
||||
|
||||
class ProfilePasswordResetView(APIView):
|
||||
@@ -618,6 +630,7 @@ class GoogleLoginAPIView(APIView):
|
||||
def post(self, request, *args, **kwargs):
|
||||
access_token = request.data.get("access_token")
|
||||
principal_type = request.data.get("principal_type")
|
||||
referral_code = request.data.get("referral_code")
|
||||
if not access_token or not principal_type:
|
||||
return Response(
|
||||
{"error": "Access token & Principal Type is required"},
|
||||
@@ -694,6 +707,47 @@ class GoogleLoginAPIView(APIView):
|
||||
principal.save()
|
||||
print("Created new user")
|
||||
message = "Details updated"
|
||||
|
||||
try:
|
||||
ReferralCode.create_referral_code_for_user_manager(
|
||||
principal=principal, principal_type=principal.principal_type
|
||||
)
|
||||
except Exception as e:
|
||||
print("ReferralCode: E-", e)
|
||||
error_response = {
|
||||
"status": status.HTTP_400_BAD_REQUEST,
|
||||
"message": constants.FAILURE,
|
||||
"errors": str(e),
|
||||
}
|
||||
return ApiResponse.error(**error_response)
|
||||
|
||||
if referral_code:
|
||||
already_register_through_referral = ReferralRecord.objects.filter(
|
||||
referred_principal=principal
|
||||
).exists()
|
||||
if not already_register_through_referral:
|
||||
try:
|
||||
whos_referral_code = ReferralCode.objects.get(
|
||||
referral_code=referral_code
|
||||
)
|
||||
except Exception as e:
|
||||
print("whos_referral_code: E-", e)
|
||||
error_response = {
|
||||
"status": status.HTTP_404_NOT_FOUND,
|
||||
"message": constants.RECORD_NOT_FOUND,
|
||||
"errors": str(e),
|
||||
}
|
||||
return ApiResponse.error(**error_response)
|
||||
|
||||
# principal_type = IAmPrincipalType.objects
|
||||
|
||||
ReferralRecord.objects.create(
|
||||
referrer_principal=whos_referral_code.principal, # principal id of the User who invited
|
||||
referred_principal=principal, # principal id of the User who join through invitation
|
||||
principal_type=whos_referral_code.principal_type, # principal type of the user who invited
|
||||
is_completed=True,
|
||||
)
|
||||
|
||||
token_data = generate_token_and_user_data(principal)
|
||||
token_data["type"] = str(principal.principal_type)
|
||||
|
||||
@@ -718,3 +772,138 @@ class GoogleLoginAPIView(APIView):
|
||||
"error": error_details,
|
||||
"status": response.status_code,
|
||||
}
|
||||
|
||||
|
||||
# Apple's public keys URL
|
||||
APPLE_PUBLIC_KEYS_URL = "https://appleid.apple.com/auth/keys"
|
||||
|
||||
# Your client ID
|
||||
AUDIENCE = "com.app.goodTimes"
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@require_http_methods(["POST"])
|
||||
def decode_apple_token(request):
|
||||
try:
|
||||
data = request.POST
|
||||
identity_token = data.get("token")
|
||||
if not identity_token:
|
||||
return JsonResponse({"error": "Token is required"}, status=400)
|
||||
|
||||
principal_type = data.get("principal_type")
|
||||
principal_type_instance = IAmPrincipalType.objects.filter(
|
||||
name=principal_type
|
||||
).first()
|
||||
if principal_type_instance is None:
|
||||
return JsonResponse(
|
||||
{"error": "No Principal Type Exists"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
# Fetch Apple's public keys
|
||||
# Note: You might want to cache these keys and update them periodically rather than fetching them with every request
|
||||
apple_keys_response = requests.get(APPLE_PUBLIC_KEYS_URL)
|
||||
apple_keys = apple_keys_response.json()
|
||||
|
||||
# Decode the token
|
||||
# Note: This is a simplified example; you should handle the selection of the key and any potential errors during decoding
|
||||
header_data = jwt.get_unverified_header(identity_token)
|
||||
kid = header_data["kid"]
|
||||
apple_key = next((key for key in apple_keys["keys"] if key["kid"] == kid), None)
|
||||
if apple_key is None:
|
||||
return JsonResponse({"error": "Invalid key ID"}, status=400)
|
||||
|
||||
# Construct the public key
|
||||
public_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(apple_key))
|
||||
|
||||
# Decode the token
|
||||
decoded = jwt.decode(
|
||||
identity_token, public_key, algorithms=["RS256"], audience=AUDIENCE
|
||||
)
|
||||
|
||||
# Check if there was an error in fetching user data
|
||||
if "error" in decoded:
|
||||
error_message = decoded.get("error", {}).get(
|
||||
"error_description", "An error occurred while fetching user data."
|
||||
)
|
||||
return JsonResponse(
|
||||
{"error": error_message},
|
||||
status=decoded.get("status", status.HTTP_400_BAD_REQUEST),
|
||||
)
|
||||
|
||||
print("decoded: ", decoded)
|
||||
|
||||
email = decoded.get("email")
|
||||
apple_id = decoded.get("sub")
|
||||
print("apple_id: ", apple_id)
|
||||
|
||||
# Checking if essential values are present and valid
|
||||
if not apple_id:
|
||||
# Return an error response if either 'email' or 'sub' is missing or empty
|
||||
return JsonResponse(
|
||||
{"error": "The token is missing required information."},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
with transaction.atomic():
|
||||
apple_source, _ = IAmPrincipalSource.objects.get_or_create(name="apple")
|
||||
principal = IAmPrincipal.objects.filter(apple_id=apple_id).first()
|
||||
print("principal: ", principal)
|
||||
if principal:
|
||||
principal.email_verified = True
|
||||
principal.principal_source = apple_source
|
||||
principal.apple_id = apple_id
|
||||
# Update any other fields that might change on each login
|
||||
principal.save()
|
||||
print("Updated existing user")
|
||||
message = "Already Registered and Verified User"
|
||||
else:
|
||||
defaults = {
|
||||
"email": f"{apple_id}@gmail.com",
|
||||
"register_complete": True,
|
||||
"email_verified": True,
|
||||
"username": apple_id, # Or generate a unique username if necessary
|
||||
"principal_source": apple_source,
|
||||
}
|
||||
|
||||
defaults["principal_type"] = principal_type_instance
|
||||
defaults["apple_id"] = apple_id
|
||||
principal = IAmPrincipal(**defaults)
|
||||
default_password = f"SEMTG{apple_id[::-1]}"
|
||||
principal.set_password(default_password)
|
||||
principal.save()
|
||||
print("Created new user")
|
||||
message = "Registered Successfully"
|
||||
token_data = generate_token_and_user_data(principal)
|
||||
token_data["type"] = str(principal.principal_type)
|
||||
|
||||
return JsonResponse({"message": message, "data": token_data}, status=200)
|
||||
|
||||
# return JsonResponse(decoded)
|
||||
except Exception as e:
|
||||
return JsonResponse({"error": str(e)}, status=400)
|
||||
|
||||
|
||||
class IAmPrincipalPlayerIDAPIView(APIView):
|
||||
authentication_classes = [JWTAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
serializer = PlayerIDSerializer(data=request.data)
|
||||
print("serializer: ", serializer)
|
||||
|
||||
if serializer.is_valid():
|
||||
principal = request.user
|
||||
principal.player_id = serializer.validated_data["player_id"]
|
||||
principal.save()
|
||||
return ApiResponse.success(
|
||||
status=status.HTTP_200_OK,
|
||||
message=constants.SUCCESS,
|
||||
data=serializer.data,
|
||||
)
|
||||
return ApiResponse.error(
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
message=constants.FAILURE,
|
||||
errors=serializer.errors,
|
||||
)
|
||||
|
||||
@@ -14,6 +14,7 @@ from accounts.resource_action import (
|
||||
RESOURCE_MANAGE_DASHBOARD,
|
||||
RESOURCE_MANAGE_IAM,
|
||||
RESOURCE_MANAGE_CUSTOMER,
|
||||
RESOURCE_MANAGE_NOTIFICATIONS,
|
||||
RESOURCE_MANAGE_WALLET,
|
||||
RESOURCE_MANAGE_PAYMENT,
|
||||
RESOURCE_MANAGE_EVENTS,
|
||||
@@ -269,4 +270,16 @@ fixture_data = [
|
||||
"action": [1, 2, 3, 4],
|
||||
},
|
||||
},
|
||||
{
|
||||
"model": "accounts.iamappresource",
|
||||
"pk": 13,
|
||||
"fields": {
|
||||
"name": RESOURCE_MANAGE_NOTIFICATIONS,
|
||||
"label": RESOURCE_MANAGE_NOTIFICATIONS,
|
||||
"slug": RESOURCE_MANAGE_NOTIFICATIONS,
|
||||
"created_on": "2023-09-28T16:17:42.815",
|
||||
"modified_on": "2023-09-28T16:17:42.815",
|
||||
"action": [1, 2, 3, 4],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
@@ -301,5 +301,22 @@
|
||||
4
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "accounts.iamappresource",
|
||||
"pk": 13,
|
||||
"fields": {
|
||||
"name": "manage_notifications",
|
||||
"label": "manage_notifications",
|
||||
"slug": "manage_notifications",
|
||||
"created_on": "2023-09-28T16:17:42.815",
|
||||
"modified_on": "2023-09-28T16:17:42.815",
|
||||
"action": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -23,6 +23,7 @@ RESOURCE_MANAGE_REPORTS = "manage_reports"
|
||||
RESOURCE_MANAGE_SUBSCRIPTIONS = "manage_subscriptions"
|
||||
RESOURCE_MANAGE_FEEDBACK = "manage_feedback"
|
||||
RESOURCE_MANAGE_REFERRALS = "manage_referrals"
|
||||
RESOURCE_MANAGE_NOTIFICATIONS = "manage_notifications"
|
||||
|
||||
|
||||
# These constants are used solely for managing the active and inactive state of pages
|
||||
|
||||
@@ -43,10 +43,10 @@ PASSWORD_CHANGE_FAILURE = "Password change failed. Please try again later."
|
||||
|
||||
|
||||
# Mobile OTP Related Constants
|
||||
PHONE_NUMBER_EXISTS = "This phone number is already in use."
|
||||
PHONE_NUMBER_EXISTS = "This email is already in use."
|
||||
EMAIL_EXISTS = "This email is already in use."
|
||||
PHONE_NUMBER_NOT_REGISTERED = "This phone number is not registered."
|
||||
EMAIL_NOT_REGISTERED = "This phone number is not registered."
|
||||
EMAIL_NOT_REGISTERED = "This email is not registered."
|
||||
PHONE_NUMBER_NOT_FOUND = "Phone number not found."
|
||||
PHONE_FIELD_IS_REQUIRED = "Phone field is required."
|
||||
PHONE_NUMBER_INVALID = 'Invalid phone number.'
|
||||
|
||||
@@ -176,6 +176,7 @@ AUTHENTICATION_BACKENDS = [
|
||||
# rest framework permission and authentication settings
|
||||
# https://www.django-rest-framework.org/api-guide/permissions/#setting-the-permission-policy
|
||||
REST_FRAMEWORK = {
|
||||
"DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",),
|
||||
# Use Django's standard `django.contrib.auth` permissions,
|
||||
# or allow read-only access for unauthenticated users.
|
||||
"DEFAULT_PERMISSION_CLASSES": [
|
||||
@@ -303,6 +304,9 @@ SIMPLE_JWT = {
|
||||
STRIPE_SECRET_KEY = env.str("STRIPE_SECRET_KEY")
|
||||
STRIPE_PUBLISH_KEY = env.str("STRIPE_PUBLISH_KEY")
|
||||
|
||||
ONE_SIGNAL_APP_ID = env.str("ONE_SIGNAL_APP_ID")
|
||||
ONE_SIGNAL_API_KEY = env.str("ONE_SIGNAL_API_KEY")
|
||||
|
||||
CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "channels_redis.core.RedisChannelLayer",
|
||||
@@ -327,26 +331,4 @@ CRONJOBS = [
|
||||
# ("0 9 * * 1-5", "manage_games.cron.update_game_status_live"),
|
||||
]
|
||||
|
||||
# GOOGLE_OAUTH2_CLIENT_ID = env.str("DJANGO_GOOGLE_OAUTH2_CLIENT_ID", default="")
|
||||
# GOOGLE_OAUTH2_CLIENT_SECRET = env.str("DJANGO_GOOGLE_OAUTH2_CLIENT_SECRET", default="")
|
||||
# GOOGLE_OAUTH2_PROJECT_ID = env.str("DJANGO_GOOGLE_OAUTH2_PROJECT_ID", default="")
|
||||
|
||||
GOOGLE_MAPS_API_KEY = env.str("GOOGLE_MAPS_API_KEY")
|
||||
|
||||
# SOCIALACCOUNT_PROVIDERS = {
|
||||
# "google": {
|
||||
# "APP": {
|
||||
# "client_id": GOOGLE_OAUTH2_CLIENT_ID, # replace me
|
||||
# "secret": GOOGLE_OAUTH2_CLIENT_SECRET, # replace me
|
||||
# "key": "", # leave empty
|
||||
# },
|
||||
# "SCOPE": [
|
||||
# "profile",
|
||||
# "email",
|
||||
# ],
|
||||
# "AUTH_PARAMS": {
|
||||
# "access_type": "online",
|
||||
# },
|
||||
# "VERIFIED_EMAIL": True,
|
||||
# },
|
||||
# }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.renderers import JSONRenderer
|
||||
from rest_framework import status
|
||||
import string
|
||||
import random
|
||||
|
||||
@@ -14,4 +14,5 @@ urlpatterns = [
|
||||
path('education/material/', views.EducationMaterialView.as_view(), name='education_material'),
|
||||
path('education/tags/', views.EducationTagsView.as_view(), name='education_tags'),
|
||||
|
||||
|
||||
]
|
||||
|
||||
@@ -11,6 +11,9 @@ from manage_cms import models
|
||||
from taggit.models import Tag
|
||||
from taggit.models import TaggedItem
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
import requests
|
||||
from django.conf import settings
|
||||
import urllib.parse
|
||||
|
||||
# from nifty11_project.services import SMSError, SMSService
|
||||
from goodtimes.utils import ApiResponse
|
||||
@@ -141,10 +144,9 @@ class EducationTagsView(APIView):
|
||||
education_content_type = ContentType.objects.get_for_model(self.model)
|
||||
|
||||
# Fetch tags associated with Education instances
|
||||
education_tags = (
|
||||
TaggedItem.objects.filter(content_type=education_content_type)
|
||||
.values_list("tag", flat=True)
|
||||
)
|
||||
education_tags = TaggedItem.objects.filter(
|
||||
content_type=education_content_type
|
||||
).values_list("tag", flat=True)
|
||||
|
||||
# Retrieve actual tag objects from Tag model using the tag IDs obtained
|
||||
queryset = Tag.objects.filter(id__in=education_tags)
|
||||
@@ -156,3 +158,4 @@ class EducationTagsView(APIView):
|
||||
"data": serializer.data,
|
||||
}
|
||||
return ApiResponse.success(**success_response)
|
||||
|
||||
|
||||
@@ -49,4 +49,5 @@ urlpatterns = [
|
||||
path('education/material/add/', views.EducationMaterialCreateOrUpdateView.as_view(), name='education_add_material'),
|
||||
path('education/material/edit/<int:pk>/', views.EducationMaterialCreateOrUpdateView.as_view(), name='education_edit_material'),
|
||||
|
||||
|
||||
]
|
||||
|
||||
@@ -26,7 +26,7 @@ from .forms import (
|
||||
FaqsForm,
|
||||
FaqCategoryFrom,
|
||||
EducationVideoForm,
|
||||
EducationMaterialForm
|
||||
EducationMaterialForm,
|
||||
)
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ class NewsArticleCreateOrUpdateView(LoginRequiredMixin, generic.View):
|
||||
resource = resource_action.RESOURCE_MANAGE_CMS
|
||||
|
||||
# Initialize the action as ACTION_CREATE (can change based on logic)
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
|
||||
template_name = "manage_cms/news_article_add.html"
|
||||
model = NewsAndArticles
|
||||
@@ -155,7 +155,7 @@ class NewsArticleCategoryCreateOrUpdateView(LoginRequiredMixin, generic.View):
|
||||
resource = resource_action.RESOURCE_MANAGE_CMS
|
||||
|
||||
# Initialize the action as ACTION_CREATE (can change based on logic)
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
|
||||
template_name = "manage_cms/news_article_category_add.html"
|
||||
model = NewsAndArticlesCategory
|
||||
@@ -321,7 +321,7 @@ class AboutUsCreateOrUpdateView(LoginRequiredMixin, generic.View):
|
||||
resource = resource_action.RESOURCE_MANAGE_CMS
|
||||
|
||||
# Initialize the action as ACTION_CREATE (can change based on logic)
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
|
||||
template_name = "manage_cms/about_us_add.html"
|
||||
model = Organization
|
||||
@@ -401,7 +401,7 @@ class TermsConditionCreateOrUpdateView(LoginRequiredMixin, generic.View):
|
||||
resource = resource_action.RESOURCE_MANAGE_CMS
|
||||
|
||||
# Initialize the action as ACTION_CREATE (can change based on logic)
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
|
||||
template_name = "manage_cms/terms_and_condition_edit.html"
|
||||
model = Organization
|
||||
@@ -467,11 +467,7 @@ class FaqListView(LoginRequiredMixin, generic.ListView):
|
||||
context_object_name = "faqs_obj"
|
||||
|
||||
def get_queryset(self):
|
||||
return (
|
||||
super()
|
||||
.get_queryset()
|
||||
.filter(deleted=False)
|
||||
)
|
||||
return super().get_queryset().filter(deleted=False)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
@@ -486,7 +482,7 @@ class FaqCreateOrUpdateView(LoginRequiredMixin, generic.View):
|
||||
resource = resource_action.RESOURCE_MANAGE_CMS
|
||||
|
||||
# Initialize the action as ACTION_CREATE (can change based on logic)
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
|
||||
template_name = "manage_cms/faq_add.html"
|
||||
model = Faqs
|
||||
@@ -551,7 +547,7 @@ class FaqCategoryCreateOrUpdateView(LoginRequiredMixin, generic.View):
|
||||
resource = resource_action.RESOURCE_MANAGE_CMS
|
||||
|
||||
# Initialize the action as ACTION_CREATE (can change based on logic)
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
|
||||
template_name = "manage_cms/faq_category_add.html"
|
||||
model = FaqCategory
|
||||
@@ -634,7 +630,7 @@ class PrivacyPolicyCreateOrUpdateView(LoginRequiredMixin, generic.View):
|
||||
resource = resource_action.RESOURCE_MANAGE_CMS
|
||||
|
||||
# Initialize the action as ACTION_CREATE (can change based on logic)
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
|
||||
template_name = "manage_cms/privacy_policy_edit.html"
|
||||
model = Organization
|
||||
@@ -723,7 +719,7 @@ class OrganizationCreateOrUpdateView(LoginRequiredMixin, generic.View):
|
||||
resource = resource_action.RESOURCE_MANAGE_CMS
|
||||
|
||||
# Initialize the action as ACTION_CREATE (can change based on logic)
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
|
||||
template_name = "manage_cms/organization_add.html"
|
||||
model = Organization
|
||||
@@ -788,18 +784,15 @@ class EducationView(LoginRequiredMixin, generic.ListView):
|
||||
context_object_name = "education_obj"
|
||||
|
||||
def get_queryset(self):
|
||||
return (
|
||||
super()
|
||||
.get_queryset()
|
||||
.prefetch_related("tags")
|
||||
.filter(deleted=False)
|
||||
)
|
||||
return super().get_queryset().prefetch_related("tags").filter(deleted=False)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["page_name"] = self.page_name
|
||||
context["video_obj"] = self.get_queryset().filter(content_type=Education.VIDEO)
|
||||
context["material_obj"] = self.get_queryset().filter(content_type=Education.MATERIAL)
|
||||
context["material_obj"] = self.get_queryset().filter(
|
||||
content_type=Education.MATERIAL
|
||||
)
|
||||
print("video data", context["video_obj"])
|
||||
return context
|
||||
|
||||
@@ -810,7 +803,7 @@ class EducationCreateOrUpdateView(LoginRequiredMixin, generic.View):
|
||||
resource = resource_action.RESOURCE_MANAGE_CMS
|
||||
|
||||
# Initialize the action as ACTION_CREATE (can change based on logic)
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
page_title = None
|
||||
|
||||
template_name = "manage_cms/education_add.html"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
from django import forms
|
||||
from .models import PushNotification, NotificationCategory, PrincipalType
|
||||
|
||||
|
||||
class PushNotificationForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = PushNotification
|
||||
fields = [
|
||||
"title",
|
||||
"notification_category",
|
||||
"banner_image",
|
||||
"principal_type",
|
||||
"message",
|
||||
]
|
||||
widgets = {
|
||||
"message": forms.Textarea(attrs={"rows": 4}),
|
||||
}
|
||||
|
||||
234
manage_notifications/migrations/0001_initial.py
Normal file
@@ -0,0 +1,234 @@
|
||||
# Generated by Django 5.0.2 on 2024-03-13 12:55
|
||||
|
||||
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="NotificationCategory",
|
||||
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)),
|
||||
("name", models.CharField(max_length=100)),
|
||||
(
|
||||
"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": "notification_settings_category",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="NotificationSettings",
|
||||
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)),
|
||||
("name", models.CharField(max_length=100)),
|
||||
(
|
||||
"category",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="notification_category",
|
||||
to="manage_notifications.notificationcategory",
|
||||
),
|
||||
),
|
||||
(
|
||||
"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": "notification_settings",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="IAmPrincipalNotificationSettings",
|
||||
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)),
|
||||
("is_enabled", models.BooleanField(default=True)),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"principal",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="notifications_principal",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"notification_setting",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="manage_notifications.notificationsettings",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"db_table": "iam_principal_notification_settings",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="PushNotification",
|
||||
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)),
|
||||
(
|
||||
"banner_image",
|
||||
models.ImageField(
|
||||
blank=True, null=True, upload_to="push_notification_images/"
|
||||
),
|
||||
),
|
||||
(
|
||||
"principal_type",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("event_user", "Event User"),
|
||||
("event_manager", "Event Manager"),
|
||||
("both", "Both"),
|
||||
],
|
||||
max_length=50,
|
||||
),
|
||||
),
|
||||
("message", models.TextField()),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"notification_category",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="push_category",
|
||||
to="manage_notifications.notificationcategory",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,57 +1,70 @@
|
||||
# from django.db import models
|
||||
# from accounts.models import BaseModel, IAmPrincipal, IAmPrincipalType
|
||||
from django.db import models
|
||||
from accounts.models import BaseModel, IAmPrincipal, IAmPrincipalType
|
||||
|
||||
|
||||
# # Create your models here.
|
||||
# class PushNotification(BaseModel):
|
||||
# title = models.CharField(max_length=255)
|
||||
# banner_image = models.ImageField(
|
||||
# upload_to="push_notification_images/", blank=True, null=True
|
||||
# )
|
||||
# principal_type = models.CharField(max_length=50)
|
||||
# message = models.TextField()
|
||||
# published_datetime = models.DateTimeField()
|
||||
# Create your models here.
|
||||
class NotificationCategory(BaseModel):
|
||||
name = models.CharField(max_length=100)
|
||||
|
||||
# def __str__(self):
|
||||
# return self.title
|
||||
class Meta:
|
||||
db_table = "notification_settings_category"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"name : {self.name}"
|
||||
|
||||
|
||||
# class NotificationSettingsCategory(BaseModel):
|
||||
# name = models.CharField(max_length=100)
|
||||
|
||||
# class Meta:
|
||||
# db_table = "notification_settings_category"
|
||||
|
||||
# def __str__(self) -> str:
|
||||
# return f"name : {self.name}"
|
||||
class PrincipalType(models.TextChoices):
|
||||
EVENT_USER = "event_user", "Event User"
|
||||
EVENT_MANAGER = "event_manager", "Event Manager"
|
||||
BOTH = "both", "Both"
|
||||
|
||||
|
||||
# class NotificationSettings(BaseModel):
|
||||
# name = models.CharField(max_length=100)
|
||||
# category = models.ForeignKey(
|
||||
# NotificationSettingsCategory,
|
||||
# on_delete=models.CASCADE,
|
||||
# related_name="notification_category",
|
||||
# )
|
||||
class PushNotification(BaseModel):
|
||||
title = models.CharField(max_length=255)
|
||||
notification_category = models.ForeignKey(
|
||||
NotificationCategory,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="push_category",
|
||||
)
|
||||
banner_image = models.ImageField(
|
||||
upload_to="push_notification_images/", blank=True, null=True
|
||||
)
|
||||
principal_type = models.CharField(
|
||||
max_length=50,
|
||||
choices=PrincipalType.choices,
|
||||
)
|
||||
message = models.TextField()
|
||||
|
||||
# class Meta:
|
||||
# db_table = "notification_settings"
|
||||
|
||||
# def __str__(self) -> str:
|
||||
# return f"name: {self.name}"
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
# class IAmPrincipalNotificationSettings(BaseModel):
|
||||
# principal = models.ForeignKey(
|
||||
# IAmPrincipal, on_delete=models.CASCADE, related_name="notifications_principal"
|
||||
# )
|
||||
# notification_setting = models.ForeignKey(
|
||||
# NotificationSettings, on_delete=models.CASCADE
|
||||
# )
|
||||
# is_enabled = models.BooleanField(default=True)
|
||||
class NotificationSettings(BaseModel):
|
||||
name = models.CharField(max_length=100)
|
||||
category = models.ForeignKey(
|
||||
NotificationCategory,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="notification_category",
|
||||
)
|
||||
|
||||
# class Meta:
|
||||
# db_table = "iam_principal_notification_settings"
|
||||
class Meta:
|
||||
db_table = "notification_settings"
|
||||
|
||||
# def __str__(self):
|
||||
# return f"{self.principal.first_name} - {self.notification_setting.name}"
|
||||
def __str__(self) -> str:
|
||||
return f"name: {self.name}"
|
||||
|
||||
|
||||
class IAmPrincipalNotificationSettings(BaseModel):
|
||||
principal = models.ForeignKey(
|
||||
IAmPrincipal, on_delete=models.CASCADE, related_name="notifications_principal"
|
||||
)
|
||||
notification_setting = models.ForeignKey(
|
||||
NotificationSettings, on_delete=models.CASCADE
|
||||
)
|
||||
is_enabled = models.BooleanField(default=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "iam_principal_notification_settings"
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.principal.first_name} - {self.notification_setting.name}"
|
||||
|
||||
@@ -6,5 +6,19 @@ from django.views.generic import TemplateView
|
||||
app_name = 'manage_notifications'
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
path(
|
||||
"notification/add/",
|
||||
views.PushNotificationsCreateOrUpdateView.as_view(),
|
||||
name="notification_add",
|
||||
),
|
||||
path(
|
||||
"notification/edit/<int:pk>/",
|
||||
views.PushNotificationsCreateOrUpdateView.as_view(),
|
||||
name="notification_edit",
|
||||
),
|
||||
path(
|
||||
"notification/list/",
|
||||
views.PushNotificationView.as_view(),
|
||||
name="notification_list",
|
||||
),
|
||||
]
|
||||
|
||||
101
manage_notifications/utils.py
Normal file
@@ -0,0 +1,101 @@
|
||||
# import requests
|
||||
# from django.conf import settings
|
||||
|
||||
# def onesignal_send_notification(notification):
|
||||
# onesignal_app_id = settings.ONE_SIGNAL_APP_ID
|
||||
# onesignal_rest_api_key = settings.ONE_SIGNAL_API_KEY
|
||||
|
||||
# headers = {
|
||||
# "Content-Type": "application/json; charset=utf-8",
|
||||
# "Authorization": f"Basic {onesignal_rest_api_key}"
|
||||
# }
|
||||
|
||||
# # Determine player IDs based on notification_category and principal_type
|
||||
# player_ids = []
|
||||
|
||||
# if notification.principal_type == PrincipalType.BOTH:
|
||||
# # Get player IDs for both event_user and event_manager
|
||||
# from .models import IAmPrincipal # Assuming your IAmPrincipal model is in the same app
|
||||
|
||||
# event_user_principals = IAmPrincipal.objects.filter(iam_principal_type__name=PrincipalType.EVENT_USER)
|
||||
# event_manager_principals = IAmPrincipal.objects.filter(iam_principal_type__name=PrincipalType.EVENT_MANAGER)
|
||||
|
||||
# for principal in event_user_principals:
|
||||
# # Assuming you have a field in IAmPrincipal that stores the player ID (e.g., 'one_signal_player_id')
|
||||
# player_ids.append(principal.one_signal_player_id)
|
||||
|
||||
# for principal in event_manager_principals:
|
||||
# player_ids.append(principal.one_signal_player_id)
|
||||
|
||||
# else:
|
||||
# # Handle filtering for EVENT_USER or EVENT_MANAGER as needed (similar logic as above)
|
||||
|
||||
# # Construct the notification payload
|
||||
# data = {
|
||||
# "app_id": onesignal_app_id,
|
||||
# "headings": {"en": notification.title},
|
||||
# "contents": {"en": notification.message},
|
||||
# "include_player_ids": player_ids, # Include the filtered player IDs
|
||||
# # Add other optional notification data according to OneSignal documentation
|
||||
# }
|
||||
|
||||
# if notification.banner_image:
|
||||
# # Include image URL if provided (requires additional OneSignal configuration)
|
||||
# data["large_icon"] = notification.banner_image.url # Assuming you have a URL property for the image field
|
||||
|
||||
# # Send the notification request to OneSignal
|
||||
# response = requests.post("https://onesignal.com/api/v1/notifications", headers=headers, json=data)
|
||||
|
||||
# if response.status_code == 200:
|
||||
# print("Notification sent successfully!")
|
||||
# else:
|
||||
# print(f"Error sending notification: {response.text}")
|
||||
|
||||
|
||||
from onesignal_sdk.client import Client as OneSignalClient
|
||||
from django.conf import settings
|
||||
from .models import IAmPrincipalNotificationSettings, NotificationSettings, IAmPrincipal
|
||||
|
||||
|
||||
def send_notification(notification_type, title, message, image_url=None):
|
||||
# Initialize OneSignal client
|
||||
onesignal_client = OneSignalClient(
|
||||
app_id=settings.ONE_SIGNAL_APP_ID, rest_api_key=settings.ONE_SIGNAL_API_KEY
|
||||
)
|
||||
|
||||
# Find all users who have enabled this type of notification
|
||||
notification_setting = NotificationSettings.objects.filter(
|
||||
name=notification_type
|
||||
).first()
|
||||
if not notification_setting:
|
||||
print("Notification type does not exist.")
|
||||
return
|
||||
|
||||
user_ids = IAmPrincipalNotificationSettings.objects.filter(
|
||||
notification_setting=notification_setting, is_enabled=True
|
||||
).values_list("principal__id", flat=True)
|
||||
|
||||
principals = (
|
||||
IAmPrincipal.objects.filter(id__in=user_ids)
|
||||
.exclude(player_id__isnull=True)
|
||||
.exclude(player_id__exact="")
|
||||
)
|
||||
|
||||
# Extract OneSignal player IDs
|
||||
player_ids = principals.values_list("player_id", flat=True)
|
||||
|
||||
# Prepare notification payload
|
||||
notification_payload = {
|
||||
"headings": {"en": title},
|
||||
"contents": {"en": message},
|
||||
"include_player_ids": list(player_ids),
|
||||
}
|
||||
|
||||
if image_url:
|
||||
notification_payload["big_picture"] = image_url
|
||||
|
||||
# Send notification
|
||||
response = onesignal_client.send_notification(notification_payload)
|
||||
print(response.status_code, response.body)
|
||||
|
||||
return response
|
||||
@@ -1,3 +1,90 @@
|
||||
from django.shortcuts import render
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse_lazy
|
||||
from django.views import generic
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib import messages
|
||||
from accounts import resource_action
|
||||
from goodtimes import constants
|
||||
from manage_notifications.forms import PushNotificationForm
|
||||
from manage_notifications.models import PushNotification
|
||||
|
||||
# Create your views here.
|
||||
|
||||
|
||||
class PushNotificationsCreateOrUpdateView(LoginRequiredMixin, generic.View):
|
||||
# Set the page_name and resource
|
||||
page_name = resource_action.RESOURCE_MANAGE_NOTIFICATIONS
|
||||
resource = resource_action.RESOURCE_MANAGE_NOTIFICATIONS
|
||||
|
||||
# Initialize the action as ACTION_CREATE (can change based on logic)
|
||||
action = resource_action.ACTION_CREATE # Default action
|
||||
|
||||
template_name = "manage_notifications/notification_add.html"
|
||||
model = PushNotification
|
||||
form_class = PushNotificationForm
|
||||
success_url = reverse_lazy("manage_notifications:notification_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 PushNotificationView(LoginRequiredMixin, generic.ListView):
|
||||
page_name = resource_action.RESOURCE_MANAGE_EVENTS
|
||||
resource = resource_action.RESOURCE_MANAGE_EVENTS
|
||||
action = resource_action.ACTION_READ
|
||||
model = PushNotification
|
||||
template_name = "manage_notifications/notification_list.html"
|
||||
context_object_name = "notification_obj"
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(deleted=False)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["page_name"] = self.page_name
|
||||
return context
|
||||
@@ -43,7 +43,8 @@ class ReferralCode(BaseModel):
|
||||
|
||||
The method ensures each generated code is unique to avoid conflicts.
|
||||
"""
|
||||
type = type.upper()
|
||||
# type = type.upper()
|
||||
type = type.replace("_", "").upper()
|
||||
name = name[:3].upper() if name else "GDTM"
|
||||
while True:
|
||||
random_number = "".join(random.choice(string.digits) for _ in range(4))
|
||||
|
||||
737
static/src/assets/css/payment/style.css
Normal file
@@ -0,0 +1,737 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap') @import url('https://fonts.googleapis.com/css2?family=Nunito+Sans:ital,opsz,wght@0,6..12,200..1000;1,6..12,200..1000&display=swap') * {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: "Poppins", sans-serif;
|
||||
}
|
||||
|
||||
:root {
|
||||
--black: #000000;
|
||||
--light-black: #050505;
|
||||
--main-yellow: rgba(209, 170, 88, 1);
|
||||
--light-yellow: rgba(229, 25, 94, 0.2);
|
||||
--white: #ffffff;
|
||||
--light-white: #f8f8f8;
|
||||
--white-other: rgba(207, 207, 207, 1);
|
||||
--white-mix: #cecece;
|
||||
--border: #ff72a285;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Poppins", sans-serif;
|
||||
}
|
||||
|
||||
.ptb {
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.sec-heading {
|
||||
font-size: 38px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
/* padding-top: 40px; */
|
||||
color: var(--main-yellow);
|
||||
}
|
||||
|
||||
.sec-subheading {
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
|
||||
.big-heading {
|
||||
font-size: 52px;
|
||||
font-weight: 700;
|
||||
color: var(--white);
|
||||
letter-spacing: 1.8px;
|
||||
}
|
||||
|
||||
|
||||
.para {
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.sec-mini-heading {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
||||
.para-dark {
|
||||
font-size: 24px;
|
||||
/* font-weight: 600; */
|
||||
}
|
||||
|
||||
.para-mid {
|
||||
font-size: 18px;
|
||||
color: rgba(255, 255, 255, 0.69);
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.pt {
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
|
||||
/* header */
|
||||
|
||||
header {
|
||||
border-bottom: 1px solid var(--main-yellow);
|
||||
position: absolute;
|
||||
background-color: transparent;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 22px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
header .header-main-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
header nav ul {
|
||||
display: flex;
|
||||
gap: 80px;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
.header-main-inner .logo {
|
||||
width: 212px;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
.header-main-inner .logo img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
header nav ul .menu-btn img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
header nav ul li a {
|
||||
color: var(--white);
|
||||
font-size: 18px;
|
||||
/* font-weight: 600; */
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sticky {
|
||||
background-color: var(--black);
|
||||
position: fixed;
|
||||
animation: slideDown 0.8s ease-out;
|
||||
-webkit-animation: slideDown 0.8s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
0% {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
header a.active {
|
||||
color: var(--main-yellow);
|
||||
}
|
||||
|
||||
header nav ul li a::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
width: 0%;
|
||||
bottom: -10px;
|
||||
border-bottom: 2px solid var(--main-yellow);
|
||||
transition: all 0.3s;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
header nav ul li a:hover {
|
||||
color: var(--main-yellow);
|
||||
}
|
||||
|
||||
header nav ul li a:hover:after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hamburger {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hamburger {
|
||||
position: relative;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hamburger img {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
|
||||
.overlay {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cross-btn {
|
||||
padding: 2px 20px;
|
||||
text-align: right;
|
||||
display: none;
|
||||
font-size: 40px;
|
||||
cursor: pointer;
|
||||
color: var(--main-yellow);
|
||||
}
|
||||
|
||||
.cross-btn i {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* about-head */
|
||||
.head-sec header,
|
||||
.terms-sec header {
|
||||
/* position: inherit; */
|
||||
background-color: var(--black);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* baner */
|
||||
.baner-section {
|
||||
background-image: linear-gradient(rgba(4, 9, 10, 0.7), rgba(4, 9, 10, 0.7)), url(images/baner.jpg);
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.baner-section .row {
|
||||
align-items: center;
|
||||
padding-top: 100px;
|
||||
}
|
||||
|
||||
.baner-section .store-app {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.baner-img {
|
||||
text-align: center;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.baner-section .baner-img img {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.baner-content .big-heading span {
|
||||
color: var(--main-yellow);
|
||||
}
|
||||
|
||||
.baner-content .grey-para {
|
||||
margin-top: 24px;
|
||||
font-size: 20px;
|
||||
color: var(--white-other);
|
||||
}
|
||||
|
||||
|
||||
.baner-btn {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.common-btn {
|
||||
background: linear-gradient(90.02deg, #CDA34C 0.02%, #F1D6A0 52%, #D1A956 98.68%);
|
||||
width: 252px;
|
||||
/* height: 50px; */
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
|
||||
/* key-feature */
|
||||
.key-features {
|
||||
background-color: #10100e;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.key-features .row {
|
||||
align-items: center;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.key-features .key-main-img img {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.key-features .key-right-first,
|
||||
.key-right-second {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.key-right-second {
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
/* easy-steps */
|
||||
.easy-steps {
|
||||
background-color: var(--black);
|
||||
}
|
||||
|
||||
.easy-steps .para-dark {
|
||||
margin-top: 50px;
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.easy-steps .para {
|
||||
color: rgba(192, 192, 192, 1);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.easy-steps-main {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 25px;
|
||||
margin-top: 100px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.easy-steps-first {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid var(--main-yellow);
|
||||
border-top-left-radius: 14px;
|
||||
border-top-right-radius: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
height: 400px;
|
||||
padding: 0px 20px;
|
||||
}
|
||||
|
||||
.easy-steps-first-img-num {
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
position: absolute;
|
||||
top: -50px;
|
||||
}
|
||||
|
||||
img.easy-steps-first-img-bot {
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
}
|
||||
|
||||
/* Adventure
|
||||
*/
|
||||
|
||||
|
||||
.Adventure {
|
||||
background-color: #10100e;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.Adventure .row {
|
||||
align-items: center;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
.Adventure-rti {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.Adventure-right img {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.Adventure-left .store-app {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.Adventure-btn {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
|
||||
/* faq */
|
||||
.faq {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
.main-faq {
|
||||
padding: 40px 0 80px;
|
||||
}
|
||||
|
||||
div#accordionExample {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.faq .accordion-item {
|
||||
border: 1px solid var(--main-yellow);
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.faq button.accordion-button:focus {
|
||||
box-shadow: inherit;
|
||||
}
|
||||
|
||||
.faq button.accordion-button {
|
||||
background-color: transparent;
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.accordion-button:not(.collapsed) {
|
||||
color: var(--main-yellow) !important;
|
||||
font-family: "Nunito Sans", sans-serif;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.accordion-item:first-of-type .accordion-button {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.accordion-button::after {
|
||||
background-image: url(images/ab.png);
|
||||
}
|
||||
|
||||
.accordion-button:not(.collapsed)::after {
|
||||
background-image: url(images/at.png);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* footer */
|
||||
.footer {
|
||||
background-color: rgba(16, 16, 14, 1);
|
||||
}
|
||||
|
||||
.footer .footer-main-img {
|
||||
width: 212px;
|
||||
height: 52px;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.footer .footer-main-img img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.footer .footer-main-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
color: var(--white);
|
||||
padding: 3rem 0 2rem;
|
||||
}
|
||||
|
||||
.footer .footer-main-grid .para-dark {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.footer .footer-main-grid .para {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
|
||||
.footer .store-app {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin: 0;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.footer-btn .common-btn {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.footer-main-grid-fourth {
|
||||
margin-top: -20px;
|
||||
}
|
||||
|
||||
.copy-right {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.store-app img {
|
||||
width: 165px;
|
||||
}
|
||||
|
||||
|
||||
/* About us */
|
||||
|
||||
.about-us {
|
||||
background-image: url(images/About\ Us\ Banner.png);
|
||||
background-position: top;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
height: 500px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 90px;
|
||||
}
|
||||
|
||||
.about-us .row {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
.about,
|
||||
.terms {
|
||||
background-color: var(--black);
|
||||
color: var(--white);
|
||||
padding: 40px 0 70px;
|
||||
}
|
||||
|
||||
|
||||
.terms-main {
|
||||
background-image: url(images/terms.png);
|
||||
background-position: top;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
height: 500px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 90px;
|
||||
}
|
||||
|
||||
|
||||
/* mediascreen */
|
||||
@media (max-width: 1199px) {
|
||||
.big-heading br {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.big-heading {
|
||||
font-size: 42px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
/* .big-heading br {
|
||||
display: none;
|
||||
} */
|
||||
|
||||
.big-heading {
|
||||
font-size: 35px;
|
||||
}
|
||||
|
||||
.store-app img {
|
||||
width: 142px;
|
||||
}
|
||||
|
||||
.sec-mini-heading {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.para {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.easy-steps-main {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 70px 20px;
|
||||
margin-top: 70px;
|
||||
}
|
||||
|
||||
.footer .footer-main-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.footer-main-grid-fourth {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.ptb {
|
||||
padding: 20px 0 40px 0;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
background-color: #60606054;
|
||||
|
||||
}
|
||||
|
||||
.cross-btn {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hamburger,
|
||||
.overlay {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navs {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
background: rgb(0 0 0);
|
||||
transition: all .3s;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.navs ul {
|
||||
flex-direction: column;
|
||||
padding: 00px 20px;
|
||||
align-items: start;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.navs ul li a {
|
||||
color: whitesmoke;
|
||||
}
|
||||
|
||||
.common-btn {
|
||||
width: 175px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
|
||||
.baner-section {
|
||||
height: inherit;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.baner-section .row {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.baner-section .store-app {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.baner-btn {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.easy-steps-main {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
|
||||
.footer .footer-main-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.easy-steps-first {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.key-features .key-main-img img {
|
||||
width: 50%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.Adventure-rti {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.Adventure .row {
|
||||
flex-direction: column-reverse;
|
||||
gap: 40px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.Adventure {
|
||||
padding: 30px 0;
|
||||
}
|
||||
|
||||
.Adventure-btn {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.Adventure-right img {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.sec-heading {
|
||||
font-size: 30px;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.about .para-mid,
|
||||
.terms .para-mid {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.faq {
|
||||
padding: 30px 0;
|
||||
}
|
||||
|
||||
.main-faq {
|
||||
padding: 20px 0 30px;
|
||||
}
|
||||
|
||||
br {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 444px) {
|
||||
/* .easy-steps-main {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
} */
|
||||
|
||||
.footer .footer-main-grid {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
gap: 0px;
|
||||
}
|
||||
|
||||
.footer .store-app {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
61
static/src/assets/js/payment/custom.js
Normal file
@@ -0,0 +1,61 @@
|
||||
// aos animation
|
||||
|
||||
AOS.init();
|
||||
|
||||
// siderbar menu
|
||||
|
||||
let cross_btn = document.querySelector('.cross-btn');
|
||||
let nav = document.querySelector('.navs');
|
||||
let ham = document.querySelector('.hamburger');
|
||||
let overlay = document.querySelector('.overlay');
|
||||
let navLinks = document.querySelector('.navLinks');
|
||||
|
||||
let openSide = function () {
|
||||
nav.style.left = '0';
|
||||
overlay.style.left = '0';
|
||||
|
||||
}
|
||||
let closeSide = function () {
|
||||
nav.style.left = '-100%';
|
||||
overlay.style.left = '-100%';
|
||||
}
|
||||
|
||||
ham.addEventListener('click', function () {
|
||||
openSide();
|
||||
})
|
||||
cross_btn.addEventListener('click', function () {
|
||||
closeSide();
|
||||
})
|
||||
overlay.addEventListener('click', function () {
|
||||
closeSide();
|
||||
})
|
||||
navLinks.addEventListener('click', function () {
|
||||
navLinks();
|
||||
})
|
||||
|
||||
|
||||
// active page
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var currentUrl = window.location.href;
|
||||
var links = document.querySelectorAll("#header nav ul li a");
|
||||
|
||||
links.forEach(function (link) {
|
||||
if (link.href === currentUrl) {
|
||||
link.classList.add("active");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// sticky header
|
||||
|
||||
const header = document.querySelector("#header");
|
||||
window.addEventListener("scroll", () => {
|
||||
const currentScroll = window.scrollY;
|
||||
if (currentScroll > 100) {
|
||||
header.classList.add('sticky');
|
||||
} else {
|
||||
header.classList.remove('sticky');
|
||||
}
|
||||
});
|
||||
@@ -148,7 +148,7 @@
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="menu">
|
||||
<!-- <li class="menu">
|
||||
<a href="./app-calendar.html" aria-expanded="false" class="dropdown-toggle">
|
||||
<div class="">
|
||||
<span class="material-symbols-outlined">analytics</span>
|
||||
@@ -163,7 +163,7 @@
|
||||
<span>Manage Coupon</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</li> -->
|
||||
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_FEEDBACK %}active{% endif %}">
|
||||
|
||||
<a href="{% url 'manage_communications:feedback_list'%}" aria-expanded="false" class="dropdown-toggle">
|
||||
@@ -182,7 +182,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li class="menu">
|
||||
<a href="./app-calendar.html" aria-expanded="false" class="dropdown-toggle">
|
||||
<a href="{% url 'manage_notifications:notification_list'%}" aria-expanded="false" class="dropdown-toggle">
|
||||
<div class="">
|
||||
<span class="material-symbols-outlined">notifications</span>
|
||||
<span>Notification</span>
|
||||
|
||||
13
templates/manage_cms/code.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<h3>
|
||||
Here you will get the CODE
|
||||
</h3>
|
||||
</body>
|
||||
</html>
|
||||
135
templates/manage_notifications/notification_add.html
Normal file
@@ -0,0 +1,135 @@
|
||||
{% 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>{{operation}} {{page_name}}</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" novalidate>
|
||||
{% 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 & 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 %}
|
||||
129
templates/manage_notifications/notification_list.html
Normal file
@@ -0,0 +1,129 @@
|
||||
{% 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 Plans</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_notifications:notification_add' %}">Send Notifications</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;"> Notification Category </th>
|
||||
<th class="checkbox-column sorting_asc" tabindex="0"
|
||||
aria-controls="style-3" aria-sort="ascending"
|
||||
style="width: 69.2656px;"> Banner Image </th>
|
||||
<th class="checkbox-column sorting_asc" tabindex="0"
|
||||
aria-controls="style-3" aria-sort="ascending"
|
||||
style="width: 69.2656px;"> Principal Type </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 notification_obj %}
|
||||
<tr role="row">
|
||||
<td class="checkbox-column text-center sorting_1"> {{data_obj.id}}</td>
|
||||
<td>{{data_obj.title}}</td>
|
||||
<td>{{data_obj.notification_category}}</td>
|
||||
<td>{{data_obj.banner_image}}</td>
|
||||
<td>{{data_obj.principal_type}}</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_subscriptions:notification_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>
|
||||
|
||||
</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": 10
|
||||
});
|
||||
|
||||
multiCheck(c3);
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
153
templates/payment/about-us.html
Normal file
@@ -0,0 +1,153 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" type="image/x-icon" href="/images/icon.png">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>Good times</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- Header -->
|
||||
<section class="head-sec">
|
||||
<header class="header-main" id="header">
|
||||
<div class="container">
|
||||
<div class="header-main-inner">
|
||||
<div class="logo">
|
||||
<a href="index.html">
|
||||
<img loading="lazy" src="images/logo.png" alt>
|
||||
</a>
|
||||
</div>
|
||||
<nav class="navs">
|
||||
<div class="cross-btn">
|
||||
×
|
||||
</div>
|
||||
<ul class="navLinks">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="about-us.html">About Us</a></li>
|
||||
<li><a href="terms.html">Terms &
|
||||
Condition</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="hamburger">
|
||||
<img loading="lazy" src="images/ham.png" alt>
|
||||
</div>
|
||||
<div class="overlay"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</section>
|
||||
|
||||
<!-- About Us page -->
|
||||
<section class="about-us">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="about-us-left">
|
||||
<h1 class="big-heading">About Us</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- about-us -->
|
||||
<section class="about">
|
||||
<div class="container">
|
||||
<p class="para-mid">Welcome to Good Times - Where Every Moment
|
||||
Counts!
|
||||
</p>
|
||||
<p class="para-mid">Good Times is an event management app founded by
|
||||
Matthew Weightman and developed by WDI. Our mission is to
|
||||
empower people to discover and engage with the vibrant events
|
||||
happening around them.
|
||||
</p>
|
||||
<p class="para-mid">Whether you're a seasoned culture enthusiast or
|
||||
someone looking to broaden your horizons, our platform is
|
||||
designed to cater to every taste and interest.
|
||||
</p>
|
||||
<p class="para-mid">But Good Times is more than just an event
|
||||
discovery app. You can earn real money by redeeming your Good
|
||||
Times points. What are the Good Times points you ask? These are
|
||||
the points you are awarded every time a new user joins the app
|
||||
using your referral code. Not just that! You receive points
|
||||
every time the user renews their subscription, giving you a
|
||||
consistent stream of income.
|
||||
</p>
|
||||
<p class="para-mid">We believe that the best experiences are those
|
||||
shared with others, and our platform facilitates connections and
|
||||
social interactions that enrich your event-going experience.
|
||||
<p class="para-mid">Whether you're searching for the hottest
|
||||
concerts, the trendiest art exhibitions, or the most
|
||||
thrilling sports events, Good Times has you covered. Join us
|
||||
on this journey of exploration, discovery, and celebration
|
||||
as we redefine the way you experience the world <br> around
|
||||
you.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- footer -->
|
||||
|
||||
<section class="footer">
|
||||
<div class="container">
|
||||
<div class="footer-main-img">
|
||||
<img loading="lazy" src="images/logo.png" alt>
|
||||
</div>
|
||||
<div class="footer-main-grid">
|
||||
<div class="footer-main-grid-first">
|
||||
<div class="store-app">
|
||||
<a href>
|
||||
<img loading="lazy" src="images/menu-left-btn.png" alt>
|
||||
</a>
|
||||
<a href class>
|
||||
<img loading="lazy" src="images/menu-right-btn.png" alt>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-main-grid-second">
|
||||
<p class="para-dark">Help</p>
|
||||
<p class="para">About Us</p>
|
||||
<p class="para">Terms & Conditions</p>
|
||||
|
||||
</div>
|
||||
<div class="footer-main-grid-third">
|
||||
<p class="para-dark">Quick Links</p>
|
||||
<p class="para">Key Features Us</p>
|
||||
<p class="para">3 Easy Steps to Stay Informed
|
||||
</p>
|
||||
</div>
|
||||
<div class="footer-main-grid-fourth">
|
||||
<div class="footer-btn">
|
||||
<button class="common-btn">Join now</button>
|
||||
</div>
|
||||
<p class="para">Let the Adventure Begin</p>
|
||||
<p class="para">FAQ
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class>
|
||||
<a href="#" class="copy-right">
|
||||
Copyright © Good Times
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous">
|
||||
</script>
|
||||
<script src="custom.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
BIN
templates/payment/images/1 inner.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
templates/payment/images/1.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
templates/payment/images/2 inner.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
templates/payment/images/2.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
templates/payment/images/3 inner.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
templates/payment/images/3.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
templates/payment/images/About Us Banner.png
Normal file
|
After Width: | Height: | Size: 7.5 MiB |
BIN
templates/payment/images/Adventure.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
templates/payment/images/Star 1.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
templates/payment/images/ab.png
Normal file
|
After Width: | Height: | Size: 228 B |
BIN
templates/payment/images/about1.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
templates/payment/images/about2.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
templates/payment/images/at.png
Normal file
|
After Width: | Height: | Size: 314 B |
BIN
templates/payment/images/baner.jpg
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
templates/payment/images/banner-mobile-cut.png
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
templates/payment/images/banner-mobile.png
Normal file
|
After Width: | Height: | Size: 4.5 MiB |
BIN
templates/payment/images/group.png
Normal file
|
After Width: | Height: | Size: 4.2 MiB |
BIN
templates/payment/images/ham.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
templates/payment/images/iPhone 14 Pro Max.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
templates/payment/images/icon.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
templates/payment/images/key mini.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
templates/payment/images/key nini 2.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
templates/payment/images/key-main.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
templates/payment/images/logo.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
templates/payment/images/menu-left-btn.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
templates/payment/images/menu-right-btn.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
templates/payment/images/terms.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
403
templates/payment/index.html
Normal file
@@ -0,0 +1,403 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" type="image/x-icon" href="/images/icon.png">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous" />
|
||||
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>Good times</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- Header -->
|
||||
<header class="header-main" id="header">
|
||||
<div class="container">
|
||||
<div class="header-main-inner">
|
||||
<div class="logo">
|
||||
<a href="index.html">
|
||||
<img loading="lazy" src="images/logo.png" alt="logo">
|
||||
</a>
|
||||
</div>
|
||||
<nav class="navs">
|
||||
<div class="cross-btn">
|
||||
×
|
||||
</div>
|
||||
<ul class="navLinks">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="about-us.html">About Us</a></li>
|
||||
<li><a href="terms.html">Terms & Condition</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="hamburger">
|
||||
<img loading="lazy" src="images/ham.png" alt>
|
||||
</div>
|
||||
<div class="overlay"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- baner -->
|
||||
|
||||
<section class="baner-section">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="baner-content" data-aos="fade-right" data-aos-duration="1000">
|
||||
<h1 class="big-heading">Discover the <span>Pulse</span>
|
||||
<br>
|
||||
of
|
||||
<span>Your City!</span>
|
||||
</h1>
|
||||
<p class="grey-para">Stay in the loop: concerts,
|
||||
festivals,
|
||||
art <br> exhibits, and more near you!</p>
|
||||
|
||||
<div class="store-app">
|
||||
<a href>
|
||||
<img loading="lazy" src="images/menu-left-btn.png" alt>
|
||||
</a>
|
||||
<a href class>
|
||||
<img loading="lazy" src="images/menu-right-btn.png" alt>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="baner-btn">
|
||||
<button class="common-btn">Join now</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="baner-img" data-aos="zoom-in-up" data-aos-duration="1000">
|
||||
<img loading="lazy" src="images/banner-mobile.png" alt>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Key Features -->
|
||||
<section class="key-features ptb">
|
||||
<div class="container">
|
||||
<h2 class="sec-heading">Key Features</h2>
|
||||
<p class="sec-subheading">Find What’s Up</p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="key-main-img text-center" data-aos="flip-left">
|
||||
<img loading="lazy" src="images/key-main.png" alt>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-6" data-aos="flip-left" data-aos-duration="1000">
|
||||
<div class="key-right-first">
|
||||
<img loading="lazy" src="images/key mini.png" alt>
|
||||
<div class="key-right-first-inner">
|
||||
<p class="sec-mini-heading">Comprehensive Event
|
||||
Listings</p>
|
||||
<p class="para">Explore all the exciting stuff
|
||||
happening in your city. From live music
|
||||
performances to theatre shows, we've got it all
|
||||
covered.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="key-right-second">
|
||||
<img loading="lazy" src="images/key nini 2.png" alt>
|
||||
<div class="key-right-second-inner">
|
||||
<p class="sec-mini-heading">Personalized
|
||||
Recommendations</p>
|
||||
<p class="para">Receive customized event suggestions
|
||||
based on your preferences and
|
||||
interests. Say goodbye to FOMO and hello to
|
||||
unforgettable experiences.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- easy-steps -->
|
||||
|
||||
<section class="easy-steps ptb">
|
||||
<div class="container">
|
||||
<h2 class="sec-heading">3 Easy Steps to Stay Informed
|
||||
</h2>
|
||||
<p class="sec-subheading">Experience the thrill of your city like never
|
||||
before <br> with Good Times.
|
||||
</p>
|
||||
<div class="easy-steps-main">
|
||||
<div class="easy-steps-first">
|
||||
<img loading="lazy" src="images/1.png" alt class="easy-steps-first-img-num">
|
||||
<h4 class="para-dark">Sign Up</h4>
|
||||
<p class="para">Create your account using your email <br> or
|
||||
social media account.
|
||||
</p>
|
||||
<img loading="lazy" src="images/1 inner.png" alt class="easy-steps-first-img-bot">
|
||||
</div>
|
||||
<div class="easy-steps-first">
|
||||
<img loading="lazy" src="images/2.png" alt class="easy-steps-first-img-num">
|
||||
<h4 class="para-dark">Subscribe</h4>
|
||||
<p class="para">Subscribe, pick your city, and share <br>
|
||||
your
|
||||
interests with us.
|
||||
</p>
|
||||
<img loading="lazy" src="images/2 inner.png" alt class="easy-steps-first-img-bot">
|
||||
</div>
|
||||
<div class="easy-steps-first">
|
||||
<img loading="lazy" src="images/3.png" alt class="easy-steps-first-img-num">
|
||||
<h4 class="para-dark">Start Exploring</h4>
|
||||
<p class="para">Discover your city’s best experiences <br>
|
||||
and
|
||||
events.
|
||||
</p>
|
||||
<img loading="lazy" src="images/3 inner.png" alt class="easy-steps-first-img-bot">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
<!-- Adventure -->
|
||||
|
||||
<section class="Adventure ptb">
|
||||
<div class="container">
|
||||
<h2 class="sec-heading">Let the Adventure Begin
|
||||
</h2>
|
||||
<p class="sec-subheading">Plan Your Outings With Confidence
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="Adventure-left">
|
||||
<div class="Adventure-rti">
|
||||
<div class="Adventure-icon">
|
||||
<img loading="lazy" src="images/Star 1.png" alt>
|
||||
</div>
|
||||
<div class="Adventure-text">Stay Informed</div>
|
||||
</div>
|
||||
<div class="Adventure-rti">
|
||||
<div class="Adventure-icon">
|
||||
<img loading="lazy" src="images/Star 1.png" alt>
|
||||
</div>
|
||||
<div class="Adventure-text">Curated
|
||||
Recommendations</div>
|
||||
</div>
|
||||
<div class="Adventure-rti">
|
||||
<div class="Adventure-icon">
|
||||
<img loading="lazy" src="images/Star 1.png" alt>
|
||||
</div>
|
||||
<div class="Adventure-text">Social Sharing</div>
|
||||
</div>
|
||||
<div class="store-app">
|
||||
<a href>
|
||||
<img loading="lazy" src="images/menu-left-btn.png" alt>
|
||||
</a>
|
||||
<a href class>
|
||||
<img loading="lazy" src="images/menu-right-btn.png" alt>
|
||||
</a>
|
||||
</div>
|
||||
<div class="Adventure-btn">
|
||||
<button class="common-btn">Join now</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="Adventure-right text-center">
|
||||
<img loading="lazy" src="images/Adventure.png" alt>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
<!-- fAQ -->
|
||||
|
||||
<section class="faq ptb">
|
||||
<div class="container">
|
||||
<h2 class="sec-heading">FAQ</h2>
|
||||
<div class="main-faq">
|
||||
<div class="accordion" id="accordionExample">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||
What types of events does Good Times cover?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseOne" class="accordion-collapse collapse show"
|
||||
data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
Good Times covers a wide range of events,
|
||||
including concerts, sporting events,
|
||||
business
|
||||
meets, tech showcases, and more. Whatever
|
||||
your interests, we've got something for you.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
|
||||
How can I find events in my city?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseTwo" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<strong>This is the second item's accordion
|
||||
body.</strong> It is hidden by default,
|
||||
until the collapse plugin adds the
|
||||
appropriate classes that we use to style
|
||||
each element. These classes control the
|
||||
overall appearance, as well as the showing
|
||||
and hiding via CSS transitions. You can
|
||||
modify any of this with custom CSS or
|
||||
overriding our default variables. It's also
|
||||
worth noting that just about any HTML can go
|
||||
within the <code>.accordion-body</code>,
|
||||
though the transition does limit overflow.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
|
||||
How does Good Times personalise event
|
||||
recommendations for me?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseThree" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<strong>This is the third item's accordion
|
||||
body.</strong> It is hidden by default,
|
||||
until the collapse plugin adds the
|
||||
appropriate classes that we use to style
|
||||
each element. These classes control the
|
||||
overall appearance, as well as the showing
|
||||
and hiding via CSS transitions. You can
|
||||
modify any of this with custom CSS or
|
||||
overriding our default variables. It's also
|
||||
worth noting that just about any HTML can go
|
||||
within the <code>.accordion-body</code>,
|
||||
though the transition does limit overflow.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#collapseFour" aria-expanded="false" aria-controls="collapseThree">
|
||||
Are there any membership fees or
|
||||
subscription charges for using Good Times?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseFour" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<strong>This is the third item's accordion
|
||||
body.</strong> It is hidden by default,
|
||||
until the collapse plugin adds the
|
||||
appropriate classes that we use to style
|
||||
each element. These classes control the
|
||||
overall appearance, as well as the showing
|
||||
and hiding via CSS transitions. You can
|
||||
modify any of this with custom CSS or
|
||||
overriding our default variables. It's also
|
||||
worth noting that just about any HTML can go
|
||||
within the <code>.accordion-body</code>,
|
||||
though the transition does limit overflow.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#collapseFive" aria-expanded="false" aria-controls="collapseThree">
|
||||
Can I use Good Times as an event manager?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseFive" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<strong>This is the third item's accordion
|
||||
body.</strong> It is hidden by default,
|
||||
until the collapse plugin adds the
|
||||
appropriate classes that we use to style
|
||||
each element. These classes control the
|
||||
overall appearance, as well as the showing
|
||||
and hiding via CSS transitions. You can
|
||||
modify any of this with custom CSS or
|
||||
overriding our default variables. It's also
|
||||
worth noting that just about any HTML can go
|
||||
within the <code>.accordion-body</code>,
|
||||
though the transition does limit overflow.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- footer -->
|
||||
|
||||
<section class="footer">
|
||||
<div class="container">
|
||||
<div class="footer-main-img">
|
||||
<img loading="lazy" src="images/logo.png" alt>
|
||||
</div>
|
||||
<div class="footer-main-grid">
|
||||
<div class="footer-main-grid-first">
|
||||
<div class="store-app">
|
||||
<a href>
|
||||
<img loading="lazy" src="images/menu-left-btn.png" alt>
|
||||
</a>
|
||||
<a href class>
|
||||
<img loading="lazy" src="images/menu-right-btn.png" alt>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-main-grid-second">
|
||||
<p class="para-dark">Help</p>
|
||||
<p class="para">About Us</p>
|
||||
<p class="para">Terms & Conditions</p>
|
||||
|
||||
</div>
|
||||
<div class="footer-main-grid-third">
|
||||
<p class="para-dark">Quick Links</p>
|
||||
<p class="para">Key Features Us</p>
|
||||
<p class="para">3 Easy Steps to Stay Informed
|
||||
</p>
|
||||
</div>
|
||||
<div class="footer-main-grid-fourth">
|
||||
<div class="footer-btn">
|
||||
<button class="common-btn">Join now</button>
|
||||
</div>
|
||||
<p class="para">Let the Adventure Begin</p>
|
||||
<p class="para">FAQ
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class>
|
||||
<a href="#" class="copy-right">
|
||||
Copyright © Good Times
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous">
|
||||
</script>
|
||||
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
|
||||
<script src="custom.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
166
templates/payment/terms.html
Normal file
@@ -0,0 +1,166 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" type="image/x-icon" href="/images/icon.png">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>Good times</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- Header -->
|
||||
<section class="terms-sec">
|
||||
<header class="header-main" id="header">
|
||||
<div class="container">
|
||||
<div class="header-main-inner">
|
||||
<div class="logo">
|
||||
<a href="index.html">
|
||||
<img loading="lazy" src="images/logo.png" alt>
|
||||
</a>
|
||||
</div>
|
||||
<nav class="navs">
|
||||
<div class="cross-btn">
|
||||
×
|
||||
</div>
|
||||
<ul class="navLinks">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="about-us.html">About Us</a></li>
|
||||
<li><a href="terms.html">Terms &
|
||||
Condition</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="hamburger">
|
||||
<img loading="lazy" src="images/ham.png" alt>
|
||||
</div>
|
||||
<div class="overlay"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</section>
|
||||
|
||||
<!-- terms page -->
|
||||
<section class="terms-main">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="about-us-left">
|
||||
<h1 class="big-heading">Terms & Condition</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- about-us -->
|
||||
<section class="terms">
|
||||
<div class="container">
|
||||
<p class="para-mid">Welcome to Good Times
|
||||
</p>
|
||||
<p class="para-mid">These terms and conditions outline the rules and
|
||||
regulations for the use of Good Times, located at Website.com.
|
||||
|
||||
</p>
|
||||
<p class="para-mid">By accessing this website we assume you accept
|
||||
these terms and conditions. Do not continue to use Good
|
||||
Times if you do not agree to take all of the terms and
|
||||
conditions stated on this page.
|
||||
|
||||
</p>
|
||||
<p class="para-mid">The following terminology applies to these Terms
|
||||
and Conditions, Privacy Statement and Disclaimer Notice and all
|
||||
Agreements: “Client”, “You” and “Your” refers to you, the
|
||||
person
|
||||
log on this website and compliant to the Company's terms and
|
||||
conditions. “The Company”, “Ourselves”, “We”, “Our” and
|
||||
“Us”,
|
||||
refers to our Company. “Party”, “Parties”, or “Us”, refers to
|
||||
both the Client and ourselves. All terms refer to the
|
||||
offer,
|
||||
acceptance and consideration of payment necessary to undertake
|
||||
the process of our assistance to the Client in the most
|
||||
appropriate manner for the express purpose of meeting the
|
||||
Client's needs in respect of provision of the Company's
|
||||
stated
|
||||
services, in accordance with and subject to, prevailing law of
|
||||
Netherlands. Any use of the above terminology or other
|
||||
words in
|
||||
the singular, plural, capitalization and/or he/she or they, are
|
||||
taken as interchangeable and therefore as referring to
|
||||
same.
|
||||
</p>
|
||||
<p class="para-mid">We employ the use of cookies. By accessing Good
|
||||
Times, you agreed to use cookies in agreement with the Good
|
||||
Times Privacyc Policy. Most interactive websites use
|
||||
cookies to let us retrieve the user's details for each visit.
|
||||
Cookies are used by our website to enable the functionality
|
||||
of certain areas to make it easier for people visiting our
|
||||
website. Some of our affiliate/advertising partners may
|
||||
also use cookies.
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- footer -->
|
||||
<section class="footer">
|
||||
<div class="container">
|
||||
<div class="footer-main-img">
|
||||
<img loading="lazy" src="images/logo.png" alt>
|
||||
</div>
|
||||
<div class="footer-main-grid">
|
||||
<div class="footer-main-grid-first">
|
||||
<!-- <div class="footer-main-grid-first-img"> -->
|
||||
<div class="store-app">
|
||||
<a href>
|
||||
<img loading="lazy" src="images/menu-left-btn.png" alt>
|
||||
</a>
|
||||
<a href class>
|
||||
<img loading="lazy" src="images/menu-right-btn.png" alt>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-main-grid-second">
|
||||
<p class="para-dark">Help</p>
|
||||
<p class="para">About Us</p>
|
||||
<p class="para">Terms & Conditions</p>
|
||||
|
||||
</div>
|
||||
<div class="footer-main-grid-third">
|
||||
<p class="para-dark">Quick Links</p>
|
||||
<p class="para">Key Features Us</p>
|
||||
<p class="para">3 Easy Steps to Stay Informed
|
||||
</p>
|
||||
</div>
|
||||
<div class="footer-main-grid-fourth">
|
||||
<div class="footer-btn">
|
||||
<button class="common-btn">Join now</button>
|
||||
</div>
|
||||
<p class="para">Let the Adventure Begin</p>
|
||||
<p class="para">FAQ
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class>
|
||||
<a href="#" class="copy-right">
|
||||
Copyright © Good Times
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous">
|
||||
</script>
|
||||
<script src="custom.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||