diff --git a/osinaweb/billing/__pycache__/admin.cpython-312.pyc b/osinaweb/billing/__pycache__/admin.cpython-312.pyc index f3040c14..001abbb5 100644 Binary files a/osinaweb/billing/__pycache__/admin.cpython-312.pyc and b/osinaweb/billing/__pycache__/admin.cpython-312.pyc differ diff --git a/osinaweb/billing/__pycache__/models.cpython-312.pyc b/osinaweb/billing/__pycache__/models.cpython-312.pyc index f486031c..fc7268cb 100644 Binary files a/osinaweb/billing/__pycache__/models.cpython-312.pyc and b/osinaweb/billing/__pycache__/models.cpython-312.pyc differ diff --git a/osinaweb/billing/__pycache__/urls.cpython-312.pyc b/osinaweb/billing/__pycache__/urls.cpython-312.pyc index 671b70ef..7ba57175 100644 Binary files a/osinaweb/billing/__pycache__/urls.cpython-312.pyc and b/osinaweb/billing/__pycache__/urls.cpython-312.pyc differ diff --git a/osinaweb/billing/__pycache__/views.cpython-312.pyc b/osinaweb/billing/__pycache__/views.cpython-312.pyc index 1cbc2833..d716243f 100644 Binary files a/osinaweb/billing/__pycache__/views.cpython-312.pyc and b/osinaweb/billing/__pycache__/views.cpython-312.pyc differ diff --git a/osinaweb/billing/add/__pycache__/urls.cpython-312.pyc b/osinaweb/billing/add/__pycache__/urls.cpython-312.pyc index 0a8b845c..4dc84b6f 100644 Binary files a/osinaweb/billing/add/__pycache__/urls.cpython-312.pyc and b/osinaweb/billing/add/__pycache__/urls.cpython-312.pyc differ diff --git a/osinaweb/billing/add/__pycache__/views.cpython-312.pyc b/osinaweb/billing/add/__pycache__/views.cpython-312.pyc index 7f983ad1..19d22306 100644 Binary files a/osinaweb/billing/add/__pycache__/views.cpython-312.pyc and b/osinaweb/billing/add/__pycache__/views.cpython-312.pyc differ diff --git a/osinaweb/billing/add/urls.py b/osinaweb/billing/add/urls.py index 8639b6a7..860eb6fe 100644 --- a/osinaweb/billing/add/urls.py +++ b/osinaweb/billing/add/urls.py @@ -6,6 +6,7 @@ urlpatterns = [ path('product', views.add_product, name='addproduct'), path('service', views.add_service, name='addservice'), path('order//', views.add_order, name='addorder'), + path('invoice-pdf//', views.add_invoice_pdf, name='addinvoice'), path('payment-pdf//', views.add_payment_pdf, name='addpayment'), diff --git a/osinaweb/billing/add/views.py b/osinaweb/billing/add/views.py index 3db2c169..56a41bd9 100644 --- a/osinaweb/billing/add/views.py +++ b/osinaweb/billing/add/views.py @@ -12,6 +12,7 @@ from weasyprint import HTML, CSS + @staff_login_required def add_product(request, *args, **kwargs): item_types = ProjectType.objects.all().order_by('name') @@ -272,6 +273,10 @@ def add_invoice_pdf(request, order_id): + + + + def add_payment_pdf(request, order_id): order = get_object_or_404(Order, id=order_id) payments = OrderPayment.objects.filter(order = order) @@ -322,3 +327,10 @@ def add_payment_pdf(request, order_id): + + + + + + + diff --git a/osinaweb/billing/delete/__pycache__/urls.cpython-312.pyc b/osinaweb/billing/delete/__pycache__/urls.cpython-312.pyc index 25d3dc41..14f9cd5b 100644 Binary files a/osinaweb/billing/delete/__pycache__/urls.cpython-312.pyc and b/osinaweb/billing/delete/__pycache__/urls.cpython-312.pyc differ diff --git a/osinaweb/billing/delete/__pycache__/views.cpython-312.pyc b/osinaweb/billing/delete/__pycache__/views.cpython-312.pyc index 6de88499..3ef75365 100644 Binary files a/osinaweb/billing/delete/__pycache__/views.cpython-312.pyc and b/osinaweb/billing/delete/__pycache__/views.cpython-312.pyc differ diff --git a/osinaweb/billing/edit/__pycache__/urls.cpython-312.pyc b/osinaweb/billing/edit/__pycache__/urls.cpython-312.pyc index 68c0a98a..e02fa7f4 100644 Binary files a/osinaweb/billing/edit/__pycache__/urls.cpython-312.pyc and b/osinaweb/billing/edit/__pycache__/urls.cpython-312.pyc differ diff --git a/osinaweb/billing/edit/__pycache__/views.cpython-312.pyc b/osinaweb/billing/edit/__pycache__/views.cpython-312.pyc index 762467ed..8be8a7c9 100644 Binary files a/osinaweb/billing/edit/__pycache__/views.cpython-312.pyc and b/osinaweb/billing/edit/__pycache__/views.cpython-312.pyc differ diff --git a/osinaweb/billing/edit/urls.py b/osinaweb/billing/edit/urls.py index 6a45915d..1bb8db0c 100644 --- a/osinaweb/billing/edit/urls.py +++ b/osinaweb/billing/edit/urls.py @@ -5,4 +5,5 @@ from billing.edit import views urlpatterns = [ path('paymentmethod//', views.edit_payment_method, name='editpaymentmethod'), path('payment/', views.edit_payment_modal, name='edit_payment_modal'), + path('update_order_status/', views.update_order_status, name='update_order_status'), ] diff --git a/osinaweb/billing/edit/views.py b/osinaweb/billing/edit/views.py index e044f77b..7c6991d7 100644 --- a/osinaweb/billing/edit/views.py +++ b/osinaweb/billing/edit/views.py @@ -32,4 +32,15 @@ def edit_payment_modal(request): } - return render(request, 'edit_templates/edit-payment-modal.html', context) \ No newline at end of file + return render(request, 'edit_templates/edit-payment-modal.html', context) + + + +@staff_login_required +def update_order_status(request): + + context = { + + } + + return render(request, 'edit_templates/update-order-status-modal.html', context) \ No newline at end of file diff --git a/osinaweb/billing/migrations/0052_alter_orderstatus_status.py b/osinaweb/billing/migrations/0052_alter_orderstatus_status.py new file mode 100644 index 00000000..487b7544 --- /dev/null +++ b/osinaweb/billing/migrations/0052_alter_orderstatus_status.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2024-05-10 06:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0051_alter_orderpayment_amount'), + ] + + operations = [ + migrations.AlterField( + model_name='orderstatus', + name='status', + field=models.CharField(choices=[('Completed', 'Completed'), ('Pending', 'Pending'), ('Failed', 'Failed'), ('Cancelled', 'Cancelled'), ('In Progress', 'In Progress')], default='Pending', max_length=200), + ), + ] diff --git a/osinaweb/billing/migrations/__pycache__/0046_remove_orderpayment_order_orderpayment_order.cpython-312.pyc b/osinaweb/billing/migrations/__pycache__/0046_remove_orderpayment_order_orderpayment_order.cpython-312.pyc new file mode 100644 index 00000000..2acd3c5c Binary files /dev/null and b/osinaweb/billing/migrations/__pycache__/0046_remove_orderpayment_order_orderpayment_order.cpython-312.pyc differ diff --git a/osinaweb/billing/migrations/__pycache__/0047_paymenttype_description_paymenttype_image.cpython-312.pyc b/osinaweb/billing/migrations/__pycache__/0047_paymenttype_description_paymenttype_image.cpython-312.pyc new file mode 100644 index 00000000..b5679024 Binary files /dev/null and b/osinaweb/billing/migrations/__pycache__/0047_paymenttype_description_paymenttype_image.cpython-312.pyc differ diff --git a/osinaweb/billing/migrations/__pycache__/0048_remove_order_status_orderstatus.cpython-312.pyc b/osinaweb/billing/migrations/__pycache__/0048_remove_order_status_orderstatus.cpython-312.pyc new file mode 100644 index 00000000..247bce7d Binary files /dev/null and b/osinaweb/billing/migrations/__pycache__/0048_remove_order_status_orderstatus.cpython-312.pyc differ diff --git a/osinaweb/billing/migrations/__pycache__/0049_rename_due_date_order_date.cpython-312.pyc b/osinaweb/billing/migrations/__pycache__/0049_rename_due_date_order_date.cpython-312.pyc new file mode 100644 index 00000000..7f2f54d0 Binary files /dev/null and b/osinaweb/billing/migrations/__pycache__/0049_rename_due_date_order_date.cpython-312.pyc differ diff --git a/osinaweb/billing/migrations/__pycache__/0050_remove_orderpayment_type_orderpayment_type.cpython-312.pyc b/osinaweb/billing/migrations/__pycache__/0050_remove_orderpayment_type_orderpayment_type.cpython-312.pyc new file mode 100644 index 00000000..ca546972 Binary files /dev/null and b/osinaweb/billing/migrations/__pycache__/0050_remove_orderpayment_type_orderpayment_type.cpython-312.pyc differ diff --git a/osinaweb/billing/migrations/__pycache__/0051_alter_orderpayment_amount.cpython-312.pyc b/osinaweb/billing/migrations/__pycache__/0051_alter_orderpayment_amount.cpython-312.pyc new file mode 100644 index 00000000..315e1623 Binary files /dev/null and b/osinaweb/billing/migrations/__pycache__/0051_alter_orderpayment_amount.cpython-312.pyc differ diff --git a/osinaweb/billing/migrations/__pycache__/0052_alter_orderstatus_status.cpython-312.pyc b/osinaweb/billing/migrations/__pycache__/0052_alter_orderstatus_status.cpython-312.pyc new file mode 100644 index 00000000..dd88ddde Binary files /dev/null and b/osinaweb/billing/migrations/__pycache__/0052_alter_orderstatus_status.cpython-312.pyc differ diff --git a/osinaweb/billing/models.py b/osinaweb/billing/models.py index ea777bfc..429a669d 100644 --- a/osinaweb/billing/models.py +++ b/osinaweb/billing/models.py @@ -63,6 +63,7 @@ class OrderStatus(models.Model): ('Pending', 'Pending'), ('Failed', 'Failed'), ('Cancelled', 'Cancelled'), + ('In Progress', 'In Progress') ) order = models.ForeignKey(Order, on_delete=models.CASCADE) status = models.CharField(max_length=200, choices=STATUS, default='Pending') diff --git a/osinaweb/billing/templates/details_templates/order-details.html b/osinaweb/billing/templates/details_templates/order-details.html index 31b54122..495c3c00 100644 --- a/osinaweb/billing/templates/details_templates/order-details.html +++ b/osinaweb/billing/templates/details_templates/order-details.html @@ -8,7 +8,7 @@
+ class="w-full bg-gray-50 px-3 py-3 border border-gray-100 shadow-md rounded-md flex flex-col md:flex-row justify-between items-center gap-3">

Order {{order.order_id}} - {{order.customer.user.first_name}} {{order.customer.user.last_name}}

@@ -18,40 +18,51 @@

{{order.status}} - {{order.orderstatus.set_all.last.status}} - {{order.orderstatus.set_all.last.date}} + {{order.orderstatus.set_all.last.status}} - + {{order.orderstatus.set_all.last.date}}

{% endif %}
+

Remaining Balance: $12.00

+
- {% if order_items %} @@ -137,7 +148,7 @@
-
+
@@ -179,9 +190,9 @@ diff --git a/osinaweb/billing/templates/edit_templates/update-order-status-modal.html b/osinaweb/billing/templates/edit_templates/update-order-status-modal.html new file mode 100644 index 00000000..3b0869b9 --- /dev/null +++ b/osinaweb/billing/templates/edit_templates/update-order-status-modal.html @@ -0,0 +1,35 @@ +{% load static %} + + + + + + + + Osina + + + + + + + + + {% csrf_token %} +

Update Order Status

+ + + +
+ +
+ + + + \ No newline at end of file diff --git a/osinaweb/billing/templates/listing_pages/invoices.html b/osinaweb/billing/templates/listing_pages/invoices.html index 6ec159d4..8a80d2b9 100644 --- a/osinaweb/billing/templates/listing_pages/invoices.html +++ b/osinaweb/billing/templates/listing_pages/invoices.html @@ -79,7 +79,7 @@ - diff --git a/osinaweb/billing/views.py b/osinaweb/billing/views.py index 5c65b6b4..85a5c2a2 100644 --- a/osinaweb/billing/views.py +++ b/osinaweb/billing/views.py @@ -63,14 +63,11 @@ def order_details(request, order_id): order_items = OrderItem.objects.filter(order=order).order_by('-id') order_item_ids = order_items.values_list('item_id', flat=True) - services = Item.objects.filter(Q(type='Service') & (Q(customer=order.customer) | Q(customer__isnull=True))).exclude(id__in=order_item_ids).order_by('-id') - products = Item.objects.filter(Q(type='Product') & (Q(customer=order.customer) | Q(customer__isnull=True))).exclude(id__in=order_item_ids).order_by('-id') payments = OrderPayment.objects.filter(order = order).order_by('-id') context = { 'order' : order, 'order_items' : order_items, - 'services': services, 'payments' : payments, } diff --git a/osinaweb/customercore/__pycache__/admin.cpython-312.pyc b/osinaweb/customercore/__pycache__/admin.cpython-312.pyc index ee7b6e08..e3b20421 100644 Binary files a/osinaweb/customercore/__pycache__/admin.cpython-312.pyc and b/osinaweb/customercore/__pycache__/admin.cpython-312.pyc differ diff --git a/osinaweb/customercore/__pycache__/custom_context.cpython-312.pyc b/osinaweb/customercore/__pycache__/custom_context.cpython-312.pyc index 4a52e963..6b2660dc 100644 Binary files a/osinaweb/customercore/__pycache__/custom_context.cpython-312.pyc and b/osinaweb/customercore/__pycache__/custom_context.cpython-312.pyc differ diff --git a/osinaweb/customercore/__pycache__/models.cpython-312.pyc b/osinaweb/customercore/__pycache__/models.cpython-312.pyc index 45316437..a5e8782b 100644 Binary files a/osinaweb/customercore/__pycache__/models.cpython-312.pyc and b/osinaweb/customercore/__pycache__/models.cpython-312.pyc differ diff --git a/osinaweb/customercore/__pycache__/urls.cpython-312.pyc b/osinaweb/customercore/__pycache__/urls.cpython-312.pyc index 86e330b2..8fe053fe 100644 Binary files a/osinaweb/customercore/__pycache__/urls.cpython-312.pyc and b/osinaweb/customercore/__pycache__/urls.cpython-312.pyc differ diff --git a/osinaweb/customercore/__pycache__/views.cpython-312.pyc b/osinaweb/customercore/__pycache__/views.cpython-312.pyc index f82fc89c..7da9af1b 100644 Binary files a/osinaweb/customercore/__pycache__/views.cpython-312.pyc and b/osinaweb/customercore/__pycache__/views.cpython-312.pyc differ diff --git a/osinaweb/customercore/migrations/__pycache__/0010_rename_ticketreaction_ticketupdatereaction.cpython-312.pyc b/osinaweb/customercore/migrations/__pycache__/0010_rename_ticketreaction_ticketupdatereaction.cpython-312.pyc new file mode 100644 index 00000000..57dd620c Binary files /dev/null and b/osinaweb/customercore/migrations/__pycache__/0010_rename_ticketreaction_ticketupdatereaction.cpython-312.pyc differ diff --git a/osinaweb/customercore/migrations/__pycache__/0011_ticketupdatereaction_customer.cpython-312.pyc b/osinaweb/customercore/migrations/__pycache__/0011_ticketupdatereaction_customer.cpython-312.pyc new file mode 100644 index 00000000..c4ab0726 Binary files /dev/null and b/osinaweb/customercore/migrations/__pycache__/0011_ticketupdatereaction_customer.cpython-312.pyc differ diff --git a/osinaweb/customercore/payment/__pycache__/urls.cpython-312.pyc b/osinaweb/customercore/payment/__pycache__/urls.cpython-312.pyc new file mode 100644 index 00000000..d97c64ac Binary files /dev/null and b/osinaweb/customercore/payment/__pycache__/urls.cpython-312.pyc differ diff --git a/osinaweb/customercore/payment/__pycache__/views.cpython-312.pyc b/osinaweb/customercore/payment/__pycache__/views.cpython-312.pyc new file mode 100644 index 00000000..d8da423e Binary files /dev/null and b/osinaweb/customercore/payment/__pycache__/views.cpython-312.pyc differ diff --git a/osinaweb/customercore/payment/urls.py b/osinaweb/customercore/payment/urls.py new file mode 100644 index 00000000..6f7d84e9 --- /dev/null +++ b/osinaweb/customercore/payment/urls.py @@ -0,0 +1,20 @@ +from django.urls import path +from . import views + + +urlpatterns = [ + path('whish-payment//', views.whish_payment, name='whishpayment'), + path('cash-payment//', views.cash_payment, name='cashpayment'), + + path('checkout//', views.card_payment, name='cardpayment'), + path('initiate-payment-checkout/', views.initiate_payment_checkout, name='initiatepaymentcheckout'), + + + path('buy-now//', views.buy_now, name='buynow'), + path('buy-now-checkout/', views.buy_now_checkout, name='buynowcheckout'), + path('check-order-status///', views.check_order_status, name='check_order_status'), + + + path('payment/osimenu-basic/', views.buy_free_osimenu, name='buyfreeosimenu'), + path('payment/osicard-basic/', views.buy_free_osicard, name='buyfreeosicard'), +] \ No newline at end of file diff --git a/osinaweb/customercore/payment/views.py b/osinaweb/customercore/payment/views.py new file mode 100644 index 00000000..a6704b15 --- /dev/null +++ b/osinaweb/customercore/payment/views.py @@ -0,0 +1,354 @@ +from django.shortcuts import render, get_object_or_404 +from customercore.decorators import * +from customercore.models import * +from billing.add.views import * +from django.http import JsonResponse +import requests +import json +import base64 +import json +import random +import string + + +def basic_auth_header(username, password): + credentials = f"merchant.{username}:{password}" + encoded_credentials = base64.b64encode(credentials.encode('utf-8')).decode('utf-8') + return f"Basic {encoded_credentials}" + + + +def whish_payment(request, payment_id): + payment = get_object_or_404(OrderPayment, id = payment_id) + + context = { + 'payment' : payment, + } + + return render(request, 'payment_pages/whish-payment.html', context) + + +def cash_payment(request, payment_id): + payment = get_object_or_404(OrderPayment, id = payment_id) + + context = { + 'payment' : payment, + } + + return render(request, 'payment_pages/cash-payment.html', context) + + + + +def card_payment(request, payment_id): + payment = get_object_or_404(OrderPayment, id = payment_id) + + context = { + 'payment' : payment, + } + + return render(request, 'payment_pages/card-payment.html', context) + + +@customer_login_required +def initiate_payment_checkout(request): + api_username = 'merchant.TEST06127800' + api_password = '37846250a67c70e7fe9f82cf6ca81f93' + merchant_id = 'TEST06127800' + merchant_name = 'Ositcom Sal' + + customer = request.user.customerprofile + + data = json.loads(request.body) + payment_id = data.get('payment_id') + + payment = get_object_or_404(OrderPayment, id=payment_id) + + + order = payment.order + order_id = order.order_id + + price = order.get_cart_total + + + payload = { + 'apiOperation': 'INITIATE_CHECKOUT', + 'apiUsername': api_username, + 'apiPassword': api_password, + 'merchant': merchant_id, + 'interaction.operation': 'PURCHASE', + 'interaction.merchant.name': merchant_name, + 'order.id': order_id, + 'order.amount': price, + 'order.currency': 'USD', + 'order.description': 'description_of_order', + 'order.notificationUrl' : 'https://newosina.osinode.com/webhooks/', + 'interaction.returnUrl' : f"https://newosina.osinode.com/check-order-status/{merchant_id}/{order_id}/" + } + + try: + response = requests.post('https://creditlibanais-netcommerce.gateway.mastercard.com/api/nvp/version/78', data=payload) + + print('Response Content:', response.text) + + if response.status_code == 200: + response_data = response.text + parsed_data = dict(item.split('=') for item in response_data.split('&')) + session_id = parsed_data.get('session.id') + return JsonResponse({'session_id': session_id}, status=200) + + else: + print('Response Status Code:', response.status_code) + return JsonResponse({'error': 'Failed to initiate checkout'}, status=500) + except Exception as e: + print('Exception:', e) + return JsonResponse({'error': 'Internal Server Error'}, status=500) + + + +@customer_login_required +def buy_now(request, item_id): + item = get_object_or_404(Item, id=item_id) + cycles = RecurringCycle.objects.filter(item = item) + + selected_cycle = None + + context = { + 'item' : item, + 'cycles' : cycles, + 'selected_cycle': selected_cycle, + } + + return render(request, 'payment_pages/buy-now.html', context) + + + +@customer_login_required +def buy_now_checkout(request): + api_username = 'merchant.TEST06127800' + api_password = '37846250a67c70e7fe9f82cf6ca81f93' + merchant_id = 'TEST06127800' + merchant_name = 'Ositcom Sal' + + customer = request.user.customerprofile + + data = json.loads(request.body) + item_id = data.get('item_id') + cycle_id = data.get('cycle_id') + + item = Item.objects.get(id=item_id) + + cycle = RecurringCycle.objects.get(id = cycle_id) + price = cycle.cycle_price + + + existing_order = Order.objects.filter(customer=customer, orderstatus__isnull=True).first() + + + if existing_order: + existing_order.orderitem_set.all().delete() + order = existing_order + else: + order = Order.objects.create(customer=customer) + + order.save() + order_id = order.order_id + + + order_item = OrderItem.objects.create(order=order, item=item, end_at = datetime.now() + timedelta(days=(cycle.months * 30))) + order_item.save() + + + payload = { + 'apiOperation': 'INITIATE_CHECKOUT', + 'apiUsername': api_username, + 'apiPassword': api_password, + 'merchant': merchant_id, + 'interaction.operation': 'PURCHASE', + 'interaction.merchant.name': merchant_name, + 'order.id': order_id, + 'order.amount': price, + 'order.currency': 'USD', + 'order.description': 'description_of_order', + 'order.notificationUrl' : 'https://newosina.osinode.com/webhooks/', + 'interaction.returnUrl' : f"https://osina.ositcom.com/check-order-status/{merchant_id}/{order_id}/" + } + + try: + response = requests.post('https://creditlibanais-netcommerce.gateway.mastercard.com/api/nvp/version/78', data=payload) + + print('Response Content:', response.text) + + if response.status_code == 200: + response_data = response.text + parsed_data = dict(item.split('=') for item in response_data.split('&')) + session_id = parsed_data.get('session.id') + return JsonResponse({'session_id': session_id}, status=200) + + else: + print('Response Status Code:', response.status_code) + return JsonResponse({'error': 'Failed to initiate checkout'}, status=500) + except Exception as e: + print('Exception:', e) + return JsonResponse({'error': 'Internal Server Error'}, status=500) + + + + + +def check_order_status(request, merchant_id, order_id): + api_password = '37846250a67c70e7fe9f82cf6ca81f93' + url = f"https://creditlibanais-netcommerce.gateway.mastercard.com/api/rest/version/78/merchant/{merchant_id}/order/{order_id}" + order = Order.objects.get(order_id=order_id) + + headers = { + 'Content-Type': 'application/json', + 'Authorization': basic_auth_header(merchant_id, api_password) + } + + try: + response = requests.get(url, headers=headers) + + if response.status_code == 200: + order_details = response.json() + if order_details.get('result') == 'SUCCESS': + OrderStatus.objects.create( + order = order, + status = 'Completed', + date = datetime.now() + ) + order_items = OrderItem.objects.filter(order=order) + for order_item in order_items: + order_item.purchased_at = datetime.now() + if order_item.item.item_type.name == 'OSIMENU': + api_url = 'https://osimenu.com/api/create-subscription/' + random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=10)) + end_date_str = order_item.end_at.strftime('%Y-%m-%d') + api_data = { + 'user': { + 'username': request.user.email, + 'email': request.user.email, + 'password': random_string, + 'first_name': request.user.first_name, + 'last_name': request.user.last_name + }, + 'customer': { + 'mobile_number': request.user.customerprofile.mobile_number + }, + 'subscription': { + 'plan': order_item.item.title, + 'end_date': end_date_str + } + } + response = requests.post(api_url, json=api_data) + order_item.active = True + old_order_items = OrderItem.objects.exclude(order__id=order.id).filter(item__item_type__name='OSIMENU', order__customer=request.user.customerprofile) + for item in old_order_items: + item.active = False + item.save() + order_item.save() + add_invoice_pdf(request, order_id=order.id) + return redirect('customerorders') + error_message = 'Failed to retrieve order details: ' + response.text + return JsonResponse({'error': error_message}, status=500) + except Exception as e: + error_message = 'Exception: ' + str(e) + return JsonResponse({'error': error_message}, status=500) + + + + +def buy_free_osimenu(request): + api_url = 'https://osimenu.com/api/create-subscription/' + random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=10)) + api_data = { + 'user': { + 'username': request.user.email, + 'email': request.user.email, + 'password': random_string, + 'first_name': request.user.first_name, + 'last_name': request.user.last_name + }, + 'customer': { + 'mobile_number': request.user.customerprofile.mobile_number + }, + 'subscription': { + 'plan': 'OSIMENU BASIC' + } + } + + order = Order.objects.create( + customer = request.user.customerprofile, + ) + + order_status = OrderStatus.objects.create( + order=order, + status = 'Completed', + date = datetime.now() + ) + + order_item = OrderItem.objects.create( + order = order, + item = get_object_or_404(Item, title='OSIMENU BASIC'), + purchased_at = datetime.now(), + active = True, + ) + + old_order_items = OrderItem.objects.exclude(order__id=order.id).filter(item__item_type__name='OSIMENU', order__customer=request.user.customerprofile) + for item in old_order_items: + item.active = False + item.save() + + response = requests.post(api_url, json=api_data) + print(response) + return redirect('customerorders') + + + + +def buy_free_osicard(request): + api_url = 'https://mybusinesscardpage.com/api/create-subscription/' + random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=10)) + api_data = { + 'user': { + 'username': request.user.email, + 'email': request.user.email, + 'password': random_string, + 'first_name': request.user.first_name, + 'last_name': request.user.last_name + }, + 'customer': { + 'mobile_number': request.user.customerprofile.mobile_number + }, + 'subscription': { + 'plan': 'OSICARD BASIC' + } + } + + order = Order.objects.create( + customer = request.user.customerprofile, + ) + + order_status = OrderStatus.objects.create( + order=order, + status = 'Completed', + date = datetime.now() + ) + + order_item = OrderItem.objects.create( + order = order, + item = get_object_or_404(Item, title='OSICARD BASIC'), + purchased_at = datetime.now(), + active = True, + ) + + old_order_items = OrderItem.objects.exclude(order__id=order.id).filter(item__item_type__name='OSICARD', order__customer=request.user.customerprofile) + for item in old_order_items: + item.active = False + item.save() + + response = requests.post(api_url, json=api_data) + print(response) + return redirect('customerorders') + + diff --git a/osinaweb/customercore/templates/details_templates/inner-customer-order.html b/osinaweb/customercore/templates/details_templates/inner-customer-order.html new file mode 100644 index 00000000..06c5439f --- /dev/null +++ b/osinaweb/customercore/templates/details_templates/inner-customer-order.html @@ -0,0 +1,151 @@ +{% extends "customer_main.html" %} +{%load static%} + +{% block content %} + +
+
+
+
+

Order {{order.order_id}}

+

+ + {% if order.orderstatus.set_all.last.status %} +

+ {{order.status}} + {{order.orderstatus.set_all.last.status}} - + {{order.orderstatus.set_all.last.date}} +

+ {% endif %} + + +
+ +

Remaining Balance: $12.00

+
+ +
+ {% if order.invoice %} + + + + {% endif %} + + + + +
+ + + {% if order_items %} +
+

Order Items

+
+ + {% for item in order_items %} +
+
+

{{item.item.title}} +

+
+
+

${{item.item.amount}} +

+
+
+ {% endfor %} +
+
+ {% endif %} + + +
+
{% if payment.date_due %} -

{{payment.date_due}}

+

{{payment.date_due}}

{% else %} -

-

+

-

{% endif %}
- +
diff --git a/osinaweb/billing/templates/listing_pages/orders.html b/osinaweb/billing/templates/listing_pages/orders.html index 7e3e96bb..7bb44744 100644 --- a/osinaweb/billing/templates/listing_pages/orders.html +++ b/osinaweb/billing/templates/listing_pages/orders.html @@ -64,7 +64,7 @@

${{order.get_cart_total}}

+

{{order.orderstatus_set.all.last.status}}

+ + + + + + + + + + + + + + {% for payment in payments %} + + + + + + + + + + + + {% endfor %} + +
+ Amount + + Date Due + + Date Paid + + Comment + + Payment +
+

${{payment.amount}}

+
+ {% if payment.date_due %} +

{{payment.date_due}}

+ {% else %} +

-

+ {% endif %} +
+ {% if payment.date_paid %} +

{{payment.date_paid}}

+ {% else %} +

UNPAID

+ {% endif %} +
+

Comment

+
+
+ {% for type in payment.type.all %} + +
+ +

{{type.name}}

+
+
+ {% endfor %} +
+
+
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/osinaweb/customercore/templates/details_templates/inner-customer-project.html b/osinaweb/customercore/templates/details_templates/inner-customer-project.html new file mode 100644 index 00000000..fe6fa6e5 --- /dev/null +++ b/osinaweb/customercore/templates/details_templates/inner-customer-project.html @@ -0,0 +1,274 @@ +{% extends "customer_main.html" %} +{%load static%} + +{% block content %} + +
+
+
+
+ +
+
+

Winabig

+

P20302

+
+
+ + +
+
+
+ +
+
+
+

20-2-2023

+

20-2-2024

+
+
+ + + +
+

Project Manager

+
+
+ +
+

Emile Elliye

+
+ +
+ + +
+

Project Details

+

There are many variations of passages of Lorem Ipsum + available, but the majority have suffered alteration + in some form, by injected humour, or randomised words which don't look even slightly believable. If you + are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden + in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks + as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 + Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks + reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or + non-characteristic words etc.

+
+ + + +
+
+
+
+

User Stories

+
+ + +
+ + + + + + + + + + + + + + + + + + + + + +
+ Story + + Related Task + + Actions +
+

requirements

+
+ dddd + +
+ + + + + + + + + + + +
+
+
+
+ + + +
+
+
+

Credentials

+
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ Email or Username + + Password + + Used For +
+

{{credential.emailorusername}}

+
+ {{credential.password}} + + {{credential.usedfor}} +
+
+
+ + + +
+
+
+
+

Related files

+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ File Name + + File + + Date Entered +
+

{{file.name}}

+
+ {{file.file}} + + {{file.date}} +
+
+
+
+
+ +{% endblock content %} \ No newline at end of file diff --git a/osinaweb/customercore/templates/listing_pages/customer-invoices.html b/osinaweb/customercore/templates/listing_pages/customer-invoices.html index 8db65278..bbe8730f 100644 --- a/osinaweb/customercore/templates/listing_pages/customer-invoices.html +++ b/osinaweb/customercore/templates/listing_pages/customer-invoices.html @@ -70,7 +70,7 @@
- + - {% endif %}
diff --git a/osinaweb/customercore/templates/listing_pages/customer-orders.html b/osinaweb/customercore/templates/listing_pages/customer-orders.html index 355dfdf6..92cd9a62 100644 --- a/osinaweb/customercore/templates/listing_pages/customer-orders.html +++ b/osinaweb/customercore/templates/listing_pages/customer-orders.html @@ -29,9 +29,12 @@ class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap"> Status - + Date Created + + Actions + @@ -54,13 +57,24 @@

${{order.get_cart_total}}

- +

{{ order.orderstatus_set.last.status }}

{{order.get_purchased_date}}

+ + + + {% endfor %} diff --git a/osinaweb/customercore/templates/listing_pages/customer-projects.html b/osinaweb/customercore/templates/listing_pages/customer-projects.html new file mode 100644 index 00000000..8ce26ad2 --- /dev/null +++ b/osinaweb/customercore/templates/listing_pages/customer-projects.html @@ -0,0 +1,89 @@ +{% extends "customer_main.html" %} +{%load static%} + +{% block content %} + + + + +{% endblock content %} \ No newline at end of file diff --git a/osinaweb/customercore/templates/payment.html b/osinaweb/customercore/templates/payment_pages/buy-now.html similarity index 98% rename from osinaweb/customercore/templates/payment.html rename to osinaweb/customercore/templates/payment_pages/buy-now.html index cd609d27..ea04885f 100644 --- a/osinaweb/customercore/templates/payment.html +++ b/osinaweb/customercore/templates/payment_pages/buy-now.html @@ -91,10 +91,10 @@ - + - + {% endblock %} \ No newline at end of file diff --git a/osinaweb/customercore/templates/invoice-payment.html b/osinaweb/customercore/templates/payment_pages/card-payment.html similarity index 92% rename from osinaweb/customercore/templates/invoice-payment.html rename to osinaweb/customercore/templates/payment_pages/card-payment.html index 7faf664e..5fe5bf4f 100644 --- a/osinaweb/customercore/templates/invoice-payment.html +++ b/osinaweb/customercore/templates/payment_pages/card-payment.html @@ -9,7 +9,7 @@ - +
@@ -38,15 +38,14 @@
-

Due Date: {{invoice.order.due_date}}

-

Invoice {{invoice.invoice_number}}

+

Due Date: {{payment.date_due}}

Bill To:

-

{{invoice.order.customer.user.first_name}} {{invoice.order.customer.user.last_name}}

-

{{invoice.order.customer.mobile_number}}

-

{{invoice.order.customer.user.email}}

+

{{payment.order.customer.user.first_name}} {{payment.order.customer.user.last_name}}

+

{{payment.order.customer.mobile_number}}

+

{{payment.order.customer.user.email}}

@@ -56,13 +55,13 @@
    - {% for item in invoice.order.orderitem_set.all %} + {% for item in payment.order.orderitem_set.all %}
  • {{item.item.title}}
  • {% endfor %}
-

Total: ${{invoice.order.get_cart_total}}

+

Total: ${{payment.order.get_cart_total}}

@@ -112,6 +111,6 @@ - + {% endblock %} \ No newline at end of file diff --git a/osinaweb/customercore/templates/payment_pages/cash-payment.html b/osinaweb/customercore/templates/payment_pages/cash-payment.html new file mode 100644 index 00000000..6ae073f8 --- /dev/null +++ b/osinaweb/customercore/templates/payment_pages/cash-payment.html @@ -0,0 +1,78 @@ +{% extends "customer_main.html" %} +{%load static%} + +{% block modules_section %} + +{% endblock modules_section %} + +{% block content %} + + + +
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + +
+ + +
+

Due Date: {{payment.date_due}}

+
+ +
+

Bill To:

+

{{payment.order.customer.user.first_name}} {{payment.order.customer.user.last_name}}

+

{{payment.order.customer.mobile_number}}

+

{{payment.order.customer.user.email}}

+
+ +
+

Invoice Items

+
+ + +
+
    + {% for item in payment.order.orderitem_set.all %} +
  • {{item.item.title}}
  • + {% endfor %} +
+ +
+

Total: ${{payment.order.get_cart_total}}

+
+
+
+
+ + + + +
+ +
+
+
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/osinaweb/customercore/templates/payment_pages/whish-payment.html b/osinaweb/customercore/templates/payment_pages/whish-payment.html new file mode 100644 index 00000000..54ae6d14 --- /dev/null +++ b/osinaweb/customercore/templates/payment_pages/whish-payment.html @@ -0,0 +1,88 @@ +{% extends "customer_main.html" %} +{%load static%} + +{% block modules_section %} + +{% endblock modules_section %} + +{% block content %} + + + +
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + +
+ + +
+

Due Date: {{payment.date_due}}

+
+ +
+

Bill To:

+

{{payment.order.customer.user.first_name}} {{payment.order.customer.user.last_name}}

+

{{payment.order.customer.mobile_number}}

+

{{payment.order.customer.user.email}}

+
+ +
+

Invoice Items

+
+ + +
+
    + {% for item in payment.order.orderitem_set.all %} +
  • {{item.item.title}}
  • + {% endfor %} +
+ +
+

Total: ${{payment.order.get_cart_total}}

+
+
+
+
+ + + + +
+
+

Scan this QR on Your Whish Mobile Application
to Proceed Payment

+ +
+ + +
+ +

Payment By Whish Money

+
+
+ +
+
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/osinaweb/customercore/templates/products/osimenu-plans.html b/osinaweb/customercore/templates/products/osimenu-plans.html index 0c7c8da1..cc0451d5 100644 --- a/osinaweb/customercore/templates/products/osimenu-plans.html +++ b/osinaweb/customercore/templates/products/osimenu-plans.html @@ -100,7 +100,7 @@
{% if not active_order_item_standard %} - + diff --git a/osinaweb/osinacore/templates/main.html b/osinaweb/osinacore/templates/main.html index cdd90b18..15fc7636 100644 --- a/osinaweb/osinacore/templates/main.html +++ b/osinaweb/osinacore/templates/main.html @@ -729,7 +729,7 @@
-

Payment Methods/p> +

Payment Methods

diff --git a/osinaweb/osinacore/urls.py b/osinaweb/osinacore/urls.py index 038caf27..72c3313b 100644 --- a/osinaweb/osinacore/urls.py +++ b/osinaweb/osinacore/urls.py @@ -42,9 +42,9 @@ urlpatterns = [ path('tickets/', views.tickets, name='tickets'), path('businesses/', views.businesses, name='businesses'), path('staffs/', views.staffs, name='users'), - path('my-projects/', views.my_projects, name='my-projects'), - path('my-tasks/', views.my_tasks, name='my-tasks'), - path('my-notes/', views.my_notes, name='my-notes'), + path('projects/', views.my_projects, name='my-projects'), + path('tasks/', views.my_tasks, name='my-tasks'), + path('notes/', views.my_notes, name='my-notes'), path('dailyreports/', views.daily_reports, name='dailyreports'), path('departments/', views.departments, name='departments'), path('projecttypes/', views.project_types, name='projecttypes'), diff --git a/osinaweb/static/dist/output.css b/osinaweb/static/dist/output.css index ef500ddf..b55d6822 100644 --- a/osinaweb/static/dist/output.css +++ b/osinaweb/static/dist/output.css @@ -1210,6 +1210,38 @@ video { height: 100vh; } +.h-\[5px\] { + height: 5px; +} + +.h-9 { + height: 2.25rem; +} + +.h-\[300px\] { + height: 300px; +} + +.h-\[250px\] { + height: 250px; +} + +.h-\[3\] { + height: 3; +} + +.h-7 { + height: 1.75rem; +} + +.h-\[65px\] { + height: 65px; +} + +.h-\[\] { + height: ; +} + .max-h-\[50px\] { max-height: 50px; } @@ -1411,6 +1443,34 @@ video { width: 100%; } +.w-\[80px\] { + width: 80px; +} + +.w-7 { + width: 1.75rem; +} + +.w-\[550px\] { + width: 550px; +} + +.w-\[65px\] { + width: 65px; +} + +.w-\[200px\] { + width: 200px; +} + +.w-\[230px\] { + width: 230px; +} + +.w-\[240px\] { + width: 240px; +} + .min-w-full { min-width: 100%; } @@ -2258,6 +2318,16 @@ video { background-color: rgb(202 138 4 / var(--tw-bg-opacity)); } +.bg-pink-700 { + --tw-bg-opacity: 1; + background-color: rgb(190 24 93 / var(--tw-bg-opacity)); +} + +.bg-yellow-300 { + --tw-bg-opacity: 1; + background-color: rgb(253 224 71 / var(--tw-bg-opacity)); +} + .bg-opacity-10 { --tw-bg-opacity: 0.1; } @@ -2453,6 +2523,10 @@ video { padding: 2.25rem; } +.p-0 { + padding: 0px; +} + .px-0 { padding-left: 0px; padding-right: 0px; @@ -2606,6 +2680,10 @@ video { padding-top: 2.25rem; } +.pb-9 { + padding-bottom: 2.25rem; +} + .text-left { text-align: left; } @@ -2871,6 +2949,10 @@ video { color: rgb(234 179 8 / var(--tw-text-opacity)); } +.text-\[xl\] { + color: xl; +} + .underline { text-decoration-line: underline; } @@ -3267,6 +3349,12 @@ video { transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); } +.hover\:scale-100:hover { + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + .hover\:bg-gray-50:hover { --tw-bg-opacity: 1; background-color: rgb(249 250 251 / var(--tw-bg-opacity)); @@ -3291,6 +3379,11 @@ video { background-color: rgb(255 255 255 / var(--tw-bg-opacity)); } +.hover\:bg-gray-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246 / var(--tw-bg-opacity)); +} + .hover\:bg-opacity-60:hover { --tw-bg-opacity: 0.6; } @@ -3309,6 +3402,11 @@ video { color: rgb(243 244 246 / var(--tw-text-opacity)); } +.hover\:text-gray-400:hover { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + .hover\:text-gray-500:hover { --tw-text-opacity: 1; color: rgb(107 114 128 / var(--tw-text-opacity)); @@ -3477,10 +3575,18 @@ video { flex-direction: row; } + .s\:items-center { + align-items: center; + } + .s\:justify-end { justify-content: flex-end; } + .s\:justify-between { + justify-content: space-between; + } + .s\:gap-0 { gap: 0px; } @@ -3851,27 +3957,10 @@ video { width: 75%; } - .xll\:w-fit { - width: -moz-fit-content; - width: fit-content; - } - .xll\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } - .xll\:flex-row { - flex-direction: row; - } - - .xll\:justify-end { - justify-content: flex-end; - } - - .xll\:justify-between { - justify-content: space-between; - } - .xll\:rounded-none { border-radius: 0px; } diff --git a/osinaweb/static/images/8992633.png b/osinaweb/static/images/8992633.png new file mode 100644 index 00000000..6456c681 Binary files /dev/null and b/osinaweb/static/images/8992633.png differ diff --git a/osinaweb/static/images/generated_invoices/attakonlogo.png b/osinaweb/static/images/generated_invoices/attakonlogo.png new file mode 100644 index 00000000..7921a2a6 Binary files /dev/null and b/osinaweb/static/images/generated_invoices/attakonlogo.png differ diff --git a/osinaweb/static/images/png-clipart-visa-mastercard-logo-visa-mastercard-computer-icons-visa-text-payment.png b/osinaweb/static/images/png-clipart-visa-mastercard-logo-visa-mastercard-computer-icons-visa-text-payment.png new file mode 100644 index 00000000..27babb18 Binary files /dev/null and b/osinaweb/static/images/png-clipart-visa-mastercard-logo-visa-mastercard-computer-icons-visa-text-payment.png differ diff --git a/osinaweb/static/images/uploaded_images/.DS_Store b/osinaweb/static/images/uploaded_images/.DS_Store new file mode 100644 index 00000000..5008ddfc Binary files /dev/null and b/osinaweb/static/images/uploaded_images/.DS_Store differ diff --git a/osinaweb/static/images/uploaded_images/attakonlogo.png b/osinaweb/static/images/uploaded_images/attakonlogo.png new file mode 100644 index 00000000..7921a2a6 Binary files /dev/null and b/osinaweb/static/images/uploaded_images/attakonlogo.png differ diff --git a/osinaweb/static/images/whish-qr.jpeg b/osinaweb/static/images/whish-qr.jpeg new file mode 100644 index 00000000..9d0a985d Binary files /dev/null and b/osinaweb/static/images/whish-qr.jpeg differ diff --git a/osinaweb/static/images/unnamed.png b/osinaweb/static/images/whishlogo.png similarity index 100% rename from osinaweb/static/images/unnamed.png rename to osinaweb/static/images/whishlogo.png diff --git a/osinaweb/static/images/whishlogo_ORNRJ4K.png b/osinaweb/static/images/whishlogo_ORNRJ4K.png new file mode 100644 index 00000000..c4a31004 Binary files /dev/null and b/osinaweb/static/images/whishlogo_ORNRJ4K.png differ diff --git a/osinaweb/static/js/customer_dashboard/payment.js b/osinaweb/static/js/customer_dashboard/payment/buy-now.js similarity index 98% rename from osinaweb/static/js/customer_dashboard/payment.js rename to osinaweb/static/js/customer_dashboard/payment/buy-now.js index b59adbfb..9ed94e8d 100644 --- a/osinaweb/static/js/customer_dashboard/payment.js +++ b/osinaweb/static/js/customer_dashboard/payment/buy-now.js @@ -11,7 +11,7 @@ function initiateCheckout(item_id, selectedCycleId) { paymentLoader.classList.remove('hidden'); - fetch('/initiate_checkout/', { + fetch('/payment/buy-now-checkout/', { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/osinaweb/static/js/customer_dashboard/payment-pricing.js b/osinaweb/static/js/customer_dashboard/payment/payment-pricing.js similarity index 100% rename from osinaweb/static/js/customer_dashboard/payment-pricing.js rename to osinaweb/static/js/customer_dashboard/payment/payment-pricing.js diff --git a/osinaweb/static/js/customer_dashboard/invoice-payment.js b/osinaweb/static/js/customer_dashboard/payment/payment.js similarity index 86% rename from osinaweb/static/js/customer_dashboard/invoice-payment.js rename to osinaweb/static/js/customer_dashboard/payment/payment.js index bdab328e..07b15e6e 100644 --- a/osinaweb/static/js/customer_dashboard/invoice-payment.js +++ b/osinaweb/static/js/customer_dashboard/payment/payment.js @@ -1,22 +1,22 @@ const paymentContent = document.getElementById('paymentContent'); const paymentLoader = document.getElementById('paymentLoader'); -const invoice_id = document.getElementById('invoiceId').textContent; +const payment_id = document.getElementById('paymentId').textContent; -function initiateInvoiceCheckout(invoice_id) { +function initiatePaymentCheckout(payment_id) { const csrftoken = getCookie('csrftoken'); - console.log(invoice_id); + console.log(payment_id); console.log('CSRF Token:', csrftoken); paymentLoader.classList.remove('hidden'); - fetch('/initiate_invoice_checkout/', { + fetch('/payment/initiate-payment-checkout/', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrftoken, }, - body: JSON.stringify({ invoice_id: invoice_id }), + body: JSON.stringify({ payment_id: payment_id }), }) .then(response => { if (!response.ok) { @@ -60,4 +60,4 @@ function getCookie(name) { return cookieValue; } -initiateInvoiceCheckout(invoice_id); +initiatePaymentCheckout(payment_id); diff --git a/osinaweb/static/js/pop-modals.js b/osinaweb/static/js/pop-modals.js index 5a861c9a..4cf461e4 100644 --- a/osinaweb/static/js/pop-modals.js +++ b/osinaweb/static/js/pop-modals.js @@ -71,6 +71,8 @@ document.addEventListener("DOMContentLoaded", function () { addButtonClickListener("editPaymentButton", "500px", "400px"); addButtonClickListener("addPaymentMethodButton", "500px", "400px"); + addButtonClickListener("updateOrderStatusButton", "400px", "160px"); + diff --git a/osinaweb/static/js/side-bar.js b/osinaweb/static/js/side-bar.js index b5d5b9f3..4e425307 100644 --- a/osinaweb/static/js/side-bar.js +++ b/osinaweb/static/js/side-bar.js @@ -123,6 +123,12 @@ menuContainers.forEach((menuContainer) => { document.querySelectorAll('.menuDropdownItems').forEach(item => { if (item !== menuDropdownItems) { item.classList.remove('active'); + + // Update the arrow icons for all other dropdowns + const otherAngleDown = item.parentElement.querySelector('.angleDown'); + const otherAngleUp = item.parentElement.querySelector('.angleUp'); + otherAngleDown.style.display = 'inline'; + otherAngleUp.style.display = 'none'; } });