{% trans 'First name' %}: {{ user.first_name|default:user.username }}
+{% trans 'Last name' %}: {{ user.last_name|default:"N/A" }}
+{% trans 'Email' %}: {{ user.email|default:"N/A" }}
+diff --git a/car_inventory/__pycache__/settings.cpython-311.pyc b/car_inventory/__pycache__/settings.cpython-311.pyc index 398c131a..165fdde1 100644 Binary files a/car_inventory/__pycache__/settings.cpython-311.pyc and b/car_inventory/__pycache__/settings.cpython-311.pyc differ diff --git a/inventory/__pycache__/forms.cpython-311.pyc b/inventory/__pycache__/forms.cpython-311.pyc index b7d0f1a6..eb66006b 100644 Binary files a/inventory/__pycache__/forms.cpython-311.pyc and b/inventory/__pycache__/forms.cpython-311.pyc differ diff --git a/inventory/__pycache__/mixins.cpython-311.pyc b/inventory/__pycache__/mixins.cpython-311.pyc index a098233c..6bcef338 100644 Binary files a/inventory/__pycache__/mixins.cpython-311.pyc and b/inventory/__pycache__/mixins.cpython-311.pyc differ diff --git a/inventory/__pycache__/models.cpython-311.pyc b/inventory/__pycache__/models.cpython-311.pyc index 17a6662e..68d5f230 100644 Binary files a/inventory/__pycache__/models.cpython-311.pyc and b/inventory/__pycache__/models.cpython-311.pyc differ diff --git a/inventory/__pycache__/urls.cpython-311.pyc b/inventory/__pycache__/urls.cpython-311.pyc index df997e16..2f40588c 100644 Binary files a/inventory/__pycache__/urls.cpython-311.pyc and b/inventory/__pycache__/urls.cpython-311.pyc differ diff --git a/inventory/__pycache__/utils.cpython-311.pyc b/inventory/__pycache__/utils.cpython-311.pyc index 6aa2a8a8..24d1001e 100644 Binary files a/inventory/__pycache__/utils.cpython-311.pyc and b/inventory/__pycache__/utils.cpython-311.pyc differ diff --git a/inventory/__pycache__/views.cpython-311.pyc b/inventory/__pycache__/views.cpython-311.pyc index e52deaf8..04480929 100644 Binary files a/inventory/__pycache__/views.cpython-311.pyc and b/inventory/__pycache__/views.cpython-311.pyc differ diff --git a/inventory/mixins.py b/inventory/mixins.py index b7762382..966a8666 100644 --- a/inventory/mixins.py +++ b/inventory/mixins.py @@ -2,17 +2,16 @@ from django import forms from django.utils.translation import get_language - class AddClassMixin: """ - Mixin for adding classes to form fields and wrapping them in a div with class 'form-floating'. + Mixin for adding classes to a model. """ def add_class_to_fields(self): """ - Adds the class to the fields of the form and wraps them in a div with class 'form-floating'. + Adds the class to the fields of the model. + :return: class names form-control or form-select """ for field_name, field in self.fields.items(): - # Add classes to the field if isinstance(field.widget, forms.Select): existing_classes = field.widget.attrs.get('class', '') field.widget.attrs['class'] = f"{existing_classes} form-select form-select-sm".strip() @@ -20,23 +19,6 @@ class AddClassMixin: existing_classes = field.widget.attrs.get('class', '') field.widget.attrs['class'] = f"{existing_classes} form-control form-control-sm".strip() - # Wrap the field in a div with class 'form-floating' - field.widget.attrs['wrapper_class'] = 'form-floating' - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.add_class_to_fields() - - def __getitem__(self, name): - """ - Overrides the __getitem__ method to wrap the field in a div with class 'form-floating'. - """ - field = super().__getitem__(name) - wrapper_class = field.field.widget.attrs.pop('wrapper_class', None) - if wrapper_class: - field = forms.utils.safety.mark_safe(f'
' + selectTimeSlotWarningTxt + '
'); + } + } +}); + +$('#staff_id').on('change', function () { + staffId = $(this).val() || null; // If staffId is an empty string, set it to null + let currentDate = null + if (selectedDate == null) { + currentDate = moment.tz(timezone).format('YYYY-MM-DD'); + } else { + currentDate = selectedDate; + } + fetchNonWorkingDays(staffId, function (newNonWorkingDays) { + nonWorkingDays = newNonWorkingDays; // Update the nonWorkingDays array + calendar.render(); // Re-render the calendar to apply changes + + // Fetch available slots for the current date + getAvailableSlots(currentDate, staffId); + }); +}); + + +function fetchNonWorkingDays(staffId, callback) { + if (!staffId || staffId === 'none') { + nonWorkingDays = []; // Reset nonWorkingDays + calendar.render(); // Re-render the calendar + callback([]); + return; // Exit the function early + } + let ajaxData = { + 'staff_member': staffId, + }; + + $.ajax({ + url: getNonWorkingDaysURL, + data: ajaxData, + dataType: 'json', + success: function (data) { + if (data.error) { + console.error('Error fetching non-working days:', data.message); + callback([]); + } else { + nonWorkingDays = data.non_working_days; + calendar.render(); + callback(data.non_working_days); + } + } + }); +} + +function getDateWithoutTime(dt) { + dt.setHours(0, 0, 0, 0); + return dt; +} + +function convertTo24Hour(time12h) { + const [time, modifier] = time12h.split(' '); + let [hours, minutes] = time.split(':'); + + if (hours === '12') { + hours = '00'; + } + + if (modifier.toUpperCase() === 'PM') { + hours = parseInt(hours, 10) + 12; + } + + return `${hours}:${minutes}`; +} + +function formatTime(date) { + const hours = date.getHours(); + const minutes = date.getMinutes(); + return (hours < 10 ? '0' + hours : hours) + ':' + (minutes < 10 ? '0' + minutes : minutes); +} + +function getAvailableSlots(selectedDate, staffId = null) { + // Update the slot list with the available slots for the selected date + const slotList = $('#slot-list'); + const slotContainer = $('.slot-container'); + const errorMessageContainer = $('.error-message'); + + // Clear previous error messages and slots + slotList.empty(); + errorMessageContainer.find('.djangoAppt_no-availability-text').remove(); + + // Remove the "Next available date" message + nextAvailableDateSelector = $('.djangoAppt_next-available-date'); // Update the selector + nextAvailableDateSelector.remove(); + + // Correctly check if staffId is 'none', null, or undefined and exit the function if true + // Check if 'staffId' is 'none', null, or undefined and display an error message + if (staffId === 'none' || staffId === null || staffId === undefined) { + console.log('No staff ID provided, displaying error message.'); + const errorMessage = $('' + noStaffMemberSelectedTxt + '
'); + errorMessageContainer.append(errorMessage); + // Optionally disable the "submit" button here + $('.btn-submit-appointment').attr('disabled', 'disabled'); + return; // Exit the function early + } + + let ajaxData = { + 'selected_date': selectedDate, + 'staff_member': staffId, + }; + fetchNonWorkingDays(staffId, function (nonWorkingDays) { + // Check if nonWorkingDays is an array + if (Array.isArray(nonWorkingDays)) { + // Update the FullCalendar configuration + // calendar.setOption('hiddenDays', nonWorkingDays); + } else { + // Handle the case where there's an error or no data + // For now, we'll just log it, but you can handle it more gracefully if needed + console.error('Failed to get non-working days:', nonWorkingDays); + } + }); + + // Send an AJAX request to get the available slots for the selected date + if (isRequestInProgress) { + return; // Exit the function if a request is already in progress + } + isRequestInProgress = true; + $.ajax({ + url: availableSlotsAjaxURL, + data: ajaxData, + dataType: 'json', + success: function (data) { + if (data.available_slots.length === 0) { + const selectedDateObj = moment.tz(selectedDate, timezone); + const selectedD = selectedDateObj.toDate(); + const today = new Date(); + today.setHours(0, 0, 0, 0); + + if (selectedD < today) { + // Show an error message + errorMessageContainer.append('' + dateInPastErrorTxt + '
'); + if (slotContainer.find('.djangoAppt_btn-request-next-slot').length === 0) { + slotContainer.append(``); + } + // Disable the 'submit' button + $('.btn-submit-appointment').attr('disabled', 'disabled'); + } else { + errorMessageContainer.find('.djangoAppt_no-availability-text').remove(); + if (errorMessageContainer.find('.djangoAppt_no-availability-text').length === 0) { + errorMessageContainer.append(`${data.message}
`); + } + // Check if the returned message is 'No availability' + if (data.message.toLowerCase() === 'no availability') { + if (slotContainer.find('.djangoAppt_btn-request-next-slot').length === 0) { + slotContainer.append(``); + } + } else { + $('.djangoAppt_btn-request-next-slot').remove(); + } + } + } else { + // remove the button to request for next available slot + $('.djangoAppt_no-availability-text').remove(); + $('.djangoAppt_btn-request-next-slot').remove(); + const uniqueSlots = [...new Set(data.available_slots)]; // remove duplicates + for (let i = 0; i < uniqueSlots.length; i++) { + slotList.append('{% trans "We've sent a verification code to your email. Please enter it below" %}:
+ +| {% trans 'Name' %} | +{% trans 'Duration' %} | +{% trans 'Price' %} | +{% trans 'Action' %} | +
|---|---|---|---|
| {{ service.name }} | +{{ service.get_duration }} | +{{ service.get_price_text }} | ++ + | +
| {% trans 'No service found' %}. | +|||
| {% trans 'Name' %} | +{% trans 'Email' %} | +{% trans 'Details' %} | +
|---|---|---|
| {{ staff_member.get_staff_member_name }} | +{{ staff_member.user.email|default:"N/A" }} | ++ {% trans 'View Profile' %} + {% trans 'Remove' %} + | +
| {% trans 'No staff members found' %}. | +||
{% trans 'First name' %}: {{ user.first_name|default:user.username }}
+{% trans 'Last name' %}: {{ user.last_name|default:"N/A" }}
+{% trans 'Email' %}: {{ user.email|default:"N/A" }}
++ {% trans 'Slot duration' %}: {{ staff_member.get_slot_duration_text }} + +
+{% trans 'General start time' %}: {{ staff_member.get_lead_time }}
+{% trans 'General end time' %}: {{ staff_member.get_finish_time }}
++ {% trans 'Weekend days you work' %}: {{ staff_member.get_weekend_days_worked_text }} +
+ ++ {% trans 'Appointment buffer time' %}: {{ staff_member.get_appointment_buffer_time_text }} + +
+ +{% trans 'No staff member information yet for this user' %}.
+| {% trans 'Start date' %} | +{% trans 'End date' %} | +{% trans 'Description' %} | +{% trans 'Action' %} | +
|---|---|---|---|
| {{ day_off.start_date }} | +{{ day_off.end_date }} | +{{ day_off.description }} | ++ + | +
| {% trans 'No days off have been set' %}. | +|||
| {% trans 'Day' %} | +{% trans 'Start time' %} | +{% trans 'End time' %} | +{% trans 'Action' %} | +
|---|---|---|---|
| {{ working_hour.get_day_of_week_str }} | +{{ working_hour.start_time|time:"g:i A" }} | +{{ working_hour.end_time|time:"g:i A" }} | ++ + | +
| {% trans 'No working hours have been set' %}. | +|||
| {% trans 'Name' %} | +{% trans 'Description' %} | +{% trans 'Duration' %} | +{% trans 'Price' %} | +{% trans 'Down payment' %} | +
|---|---|---|---|---|
| {{ service.name }} | +{{ service.description|default:"N/A" }} | +{{ service.get_duration }} | +{{ service.get_price_text }} | +{{ service.get_down_payment_text }} | +
| {% trans 'No service offered yet' %}. | +||||
{% trans "Appointment details" %}:
+{% trans "We've sent a verification code to your email. Please enter it below" %}:
+ + + + {% if messages %} + {% for message in messages %} + + {% endfor %} + {% endif %} +{% translate 'Dear Admin,' %}
+{% translate 'You have received a new appointment request. Here are the details:' %}
+ +{% translate 'Client Name' %}: {{ client_name }}
+{% translate 'Service Requested' %}: {{ appointment.get_service_name }}
+{% translate 'Appointment Date' %}: {{ appointment.appointment_request.date }}
+{% translate 'Time' %}: {{ appointment.appointment_request.start_time }} + - {{ appointment.appointment_request.end_time }}
+{% translate 'Contact Details' %}: {{ appointment.phone }} | {{ appointment.client.email }}
+{% translate 'Additional Info' %}: {{ appointment.additional_info|default:"N/A" }}
+{% translate 'Please review the appointment request and take the necessary action.' %}
+ + ++ {% if recipient_type == 'client' %} + {% translate 'Dear' %} {{ first_name }}, + {% else %} + {% translate 'Dear Administrator,' %} + {% endif %} +
+{% translate 'This is a reminder for your upcoming appointment.' %}
+{% translate 'Service' %}: {{ appointment.get_service_name }}
+{% translate 'Date' %}: {{ appointment.appointment_request.date }}
+{% translate 'Time' %}: {{ appointment.appointment_request.start_time }} + - {{ appointment.appointment_request.end_time }}
+{% translate 'Location' %}: {{ appointment.address }}
+ {% if recipient_type == 'client' %} +{% translate 'If you need to reschedule, please click the button below or contact us for further assistance.' %}
+ {% translate 'Reschedule Appointment' %} +{% translate 'Thank you for choosing us!' %}
+ {% else %} +{% translate 'Please ensure the appointment setup is complete and ready for the client.' %}
+ {% endif %} +{% trans "Dear" %} {{ first_name }},
+ {% else %} +{% trans "Hi" %},
+ {% endif %} + + {% if is_confirmation %} ++ {% trans "You have requested to reschedule your appointment. Please review the changes below and confirm:" %} +
+ {% else %} +
+ {% trans "An appointment with" %} {{ client_name }} {% trans "for the service" %}
+ {{ service_name }} {% trans "has been rescheduled." %}
+ {% if reason_for_rescheduling %}
+
{% trans "Reason for rescheduling:" %}
+ {{ reason_for_rescheduling }}{% endif %}
+
+ {% trans "Original Appointment:" %}
+ {% trans "Date" %}: {{ old_date }}
+ {% trans "Time" %}: {{ old_start_time }} {% trans ' to ' %} {{ old_end_time }}
+
+ {% trans "Rescheduled Appointment:" %}
+ {% trans "Date" %}: {{ reschedule_date }}
+ {% trans "Time" %}: {{ start_time }} {% trans ' to ' %} {{ end_time }}
+
+ {% trans "This link will expire in 5 minutes. If you do not confirm within this time frame, you will need to submit a new reschedule request." %} +
+ + {% trans "Confirm Appointment" %} + +
+
+
|
+ |||||||
+
+
|
+ |||||||
+
+
|
+
{{ cars.first.id_car_serie.name }}, {{ cars.first.id_car_trim.name }}
{% trans 'Paid Amount' %}
-{% trans 'Due Amount' %}
{% if invoice.is_paid %} -Thank you for choosing us. We appreciate your business!
+ +invoice Number: #{{invoice.invoice_number}}
-Date: {{invoice.date_in_review}}
-Customer: {{invoice.customer.customer_name}}
-Email: {{invoice.customer.email}}
-Terms: {{invoice.terms|title}}
+| + Invoice Number + | ++ {{invoice.invoice_number}} + | ++ رقم الفاتورة + | +
| + Date + | ++ {{invoice.date_in_review}} + | ++ التاريخ + | +
| + Customer + | ++ {{invoice.customer.customer_name}} + | ++ العميل + | +
| + Email + | ++ {{invoice.customer.email}} + | ++ البريد الالكتروني + | +
| + Terms + | ++ {{invoice.get_terms_display}} + | ++ طريقة الدفع + | +
| Item | -Quantity | -Unit Price | -Total | +Item الصنف |
+ Quantity العدد |
+ Unit Price سعر الوحدة |
+ Total الإجمالي |
{{item.info.make}} | {{item.quantity}} | {{item.finances.selling_price}} | -{{item.total}} | +{{item.total}} | {% endfor %} @@ -210,28 +222,29 @@
|---|