Merge branch '28-prepraviti-payment-page-da-podrzava-kodove' into 'master'
dodata validacija koda na payment page, 10 second delay za ispisivanje provere Closes #28 See merge request kbr4/riskletpy!33
This commit was merged in pull request #82.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{% extends "admin/change_list.html" %}
|
{% extends "admin/change_list.html" %}
|
||||||
{% block object-tools %}
|
{% block object-tools %}
|
||||||
<div style="padding: 12px 0; display: flex; gap: 8px;">
|
<div style="padding: 12px 0;">
|
||||||
<div style="padding: 12px 0;">
|
<div style="padding: 12px 0;">
|
||||||
<a href="{% url 'admin:generate-codes' %}" class="button">Generate Payment Codes</a>
|
<a href="{% url 'admin:generate-codes' %}" class="button">Generate Payment Codes</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,14 +10,19 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<form method="post" class="space-y-6">
|
<form method="post" class="space-y-6">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input
|
<div class="relative flex items-center">
|
||||||
type="text"
|
<input
|
||||||
name="code"
|
type="text"
|
||||||
maxlength="10"
|
id="code-input"
|
||||||
class="w-full px-4 py-3 border-2 border-accent rounded-lg focus:outline-none focus:ring-2 focus:ring-accent text-lg tracking-widest text-center font-mono mb-2"
|
name="code"
|
||||||
placeholder="Enter your code"
|
maxlength="10"
|
||||||
required
|
class="w-full px-4 py-3 border-2 border-accent rounded-lg focus:outline-none focus:ring-2 focus:ring-accent text-lg tracking-widest text-center font-mono mb-2"
|
||||||
>
|
placeholder="Enter your code"
|
||||||
|
required
|
||||||
|
autocomplete="off"
|
||||||
|
>
|
||||||
|
<span id="code-status" class="absolute right-3 top-1/2 -translate-y-1/2 text-2xl"></span>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="w-full bg-accent text-primary hover:bg-yellow-400 font-bold py-3 px-8 rounded-lg shadow-lg text-lg transition-all duration-200 ease-in-out transform hover:scale-105"
|
class="w-full bg-accent text-primary hover:bg-yellow-400 font-bold py-3 px-8 rounded-lg shadow-lg text-lg transition-all duration-200 ease-in-out transform hover:scale-105"
|
||||||
@@ -25,9 +30,53 @@
|
|||||||
Enter Code
|
Enter Code
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
<p id="code-error" class="mt-6 font-semibold text-lg"></p>
|
||||||
{% if error %}
|
{% if error %}
|
||||||
<p class="text-red-600 mt-6 font-semibold text-lg">{{ error }}</p>
|
<p id="backend-error" class="text-red-600 mt-6 font-semibold text-lg">{{ error }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const input = document.getElementById('code-input');
|
||||||
|
const status = document.getElementById('code-status');
|
||||||
|
const codeError = document.getElementById('code-error');
|
||||||
|
const backendError = document.getElementById('backend-error');
|
||||||
|
|
||||||
|
input.addEventListener('input', function() {
|
||||||
|
status.innerHTML = '';
|
||||||
|
codeError.innerHTML = '';
|
||||||
|
if (backendError) backendError.style.display = 'none';
|
||||||
|
const code = input.value.trim();
|
||||||
|
if (code.length === 0) return;
|
||||||
|
|
||||||
|
status.innerHTML = `<svg class="animate-spin h-6 w-6 text-accent" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||||
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||||
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8z"></path>
|
||||||
|
</svg>`;
|
||||||
|
|
||||||
|
fetch("{% url 'core:validate_code' %}", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-CSRFToken": "{{ csrf_token }}",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ code: code })
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
status.innerHTML = '';
|
||||||
|
if (data.valid) {
|
||||||
|
codeError.innerHTML = '<span class="text-green-600">✅ Valid code</span>';
|
||||||
|
} else {
|
||||||
|
codeError.innerHTML = '<span class="text-red-600">❌ Invalid code</span>';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
status.innerHTML = '';
|
||||||
|
codeError.innerHTML = '<span class="text-red-600">❌ Error checking code</span>';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -16,6 +16,7 @@ urlpatterns = [
|
|||||||
path('api/validate_form_fields/', v.validate_form_fields, name='validate_form_fields'),
|
path('api/validate_form_fields/', v.validate_form_fields, name='validate_form_fields'),
|
||||||
path('no_confidential_data/', v.no_confidential_data, name='no_confidential_data'),
|
path('no_confidential_data/', v.no_confidential_data, name='no_confidential_data'),
|
||||||
path('downloads/risklet_example_document.pdf', v.download_example_pdf, name='download_example_pdf'),
|
path('downloads/risklet_example_document.pdf', v.download_example_pdf, name='download_example_pdf'),
|
||||||
|
path('validate_code/', v.validate_code, name='validate_code'),
|
||||||
|
|
||||||
#admin urls
|
#admin urls
|
||||||
path('admin/payment-codes-pdf/', v.payment_codes_pdf_view, name='payment_codes_pdf'),
|
path('admin/payment-codes-pdf/', v.payment_codes_pdf_view, name='payment_codes_pdf'),
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import logging
|
import logging
|
||||||
import yaml
|
import yaml
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
from django.shortcuts import render, redirect , get_object_or_404
|
from django.shortcuts import render, redirect , get_object_or_404
|
||||||
from .forms import OrganizationForm
|
from .forms import OrganizationForm
|
||||||
@@ -9,15 +12,14 @@ from django.contrib.admin.views.decorators import staff_member_required
|
|||||||
from .utils import generate_pdf, generate_risk_graph, generate_residual_risk_graph
|
from .utils import generate_pdf, generate_risk_graph, generate_residual_risk_graph
|
||||||
from .tables import risk_matrix_table ,get_risk_table, get_safeguard_summary_table
|
from .tables import risk_matrix_table ,get_risk_table, get_safeguard_summary_table
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
site_domain = settings.SITE_DOMAIN
|
|
||||||
from .processors import render_template
|
from .processors import render_template
|
||||||
from django.http import JsonResponse, FileResponse, Http404, HttpResponse
|
from django.http import JsonResponse, FileResponse, Http404, HttpResponse
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.validators import validate_email
|
from django.core.validators import validate_email
|
||||||
import os
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from weasyprint import HTML
|
from weasyprint import HTML
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
|
|
||||||
# @login_required
|
# @login_required
|
||||||
@@ -25,6 +27,7 @@ from django.template.loader import render_to_string
|
|||||||
# return HttpResponse('<h1>Django</h1><p>Página simples.</p>')
|
# return HttpResponse('<h1>Django</h1><p>Página simples.</p>')
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
site_domain = settings.SITE_DOMAIN
|
||||||
|
|
||||||
# @login_required
|
# @login_required
|
||||||
def index(request):
|
def index(request):
|
||||||
@@ -134,7 +137,7 @@ def payment_page(request):
|
|||||||
send_document_email(email, url, document)
|
send_document_email(email, url, document)
|
||||||
return redirect(url)
|
return redirect(url)
|
||||||
except PaymentCode.DoesNotExist:
|
except PaymentCode.DoesNotExist:
|
||||||
error = "CODE INVALID"
|
error = "❌ Invalid code"
|
||||||
return render(request, 'payment.html', {'error': error})
|
return render(request, 'payment.html', {'error': error})
|
||||||
|
|
||||||
def no_confidential_data(request):
|
def no_confidential_data(request):
|
||||||
@@ -161,4 +164,18 @@ def payment_codes_pdf_view(request):
|
|||||||
pdf_content = HTML(string=html_string, base_url=request.build_absolute_uri('/')).write_pdf()
|
pdf_content = HTML(string=html_string, base_url=request.build_absolute_uri('/')).write_pdf()
|
||||||
response = HttpResponse(pdf_content, content_type='application/pdf')
|
response = HttpResponse(pdf_content, content_type='application/pdf')
|
||||||
response['Content-Disposition'] = f'inline; filename=payment_codes_{timezone.now().strftime("%Y%m%d_%H%M%S")}.pdf'
|
response['Content-Disposition'] = f'inline; filename=payment_codes_{timezone.now().strftime("%Y%m%d_%H%M%S")}.pdf'
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
def validate_code(request):
|
||||||
|
if request.method == "POST":
|
||||||
|
try:
|
||||||
|
data = json.loads(request.body)
|
||||||
|
code = data.get("code", "").strip().upper()
|
||||||
|
from .models import PaymentCode
|
||||||
|
valid = PaymentCode.objects.filter(code=code, used=False).exists()
|
||||||
|
time.sleep(10)
|
||||||
|
return JsonResponse({"valid": valid})
|
||||||
|
except Exception:
|
||||||
|
return JsonResponse({"valid": False})
|
||||||
|
return JsonResponse({"valid": False})
|
||||||
Reference in New Issue
Block a user