newClientModal.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <?php
  2. /**
  3. * components/newClientModal.php
  4. *
  5. * Modal dialog for adding new clients.
  6. * Replaces modX [[!newClientDetails]] snippet.
  7. */
  8. // Start session if not already started
  9. if (session_status() === PHP_SESSION_NONE) {
  10. session_start();
  11. }
  12. // Include CSRF library
  13. require_once __DIR__ . '/../lib/csrf.php';
  14. ?>
  15. <script type="text/javascript">
  16. $(function(){
  17. $('#email').click(function(){
  18. $('#newClient').modal('show');
  19. return false;
  20. });
  21. // Also allow clicking on the "add new client" option in the dropdown
  22. $('#selectUsers').on('change', function() {
  23. if ($(this).val() === 'new') {
  24. $('#newClient').modal('show');
  25. $(this).val(''); // Reset dropdown
  26. return false;
  27. }
  28. });
  29. });
  30. </script>
  31. <!-- Modal -->
  32. <div class="modal fade" id="newClient" tabindex="-1" role="dialog" aria-labelledby="newClientLabel" aria-hidden="true">
  33. <div class="modal-dialog modal-lg">
  34. <div class="modal-content">
  35. <div class="modal-header">
  36. <h5 class="modal-title" id="newClientLabel">Add New Client</h5>
  37. <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
  38. </div>
  39. <div class="modal-body">
  40. <form method="post" action="/controllers/newClientSubmit.php" id="newClientDetails" novalidate>
  41. <input type="hidden" name="csrf_token" value="<?php echo generateCsrfToken(); ?>">
  42. <input type="hidden" name="mod_user" id="mod_user" value="<?php echo htmlspecialchars($_SESSION['user_id'] ?? '', ENT_QUOTES, 'UTF-8'); ?>" required>
  43. <input type="hidden" name="modx_user_attributes" id="modx_user_attributes" value="<?php echo htmlspecialchars($_SESSION['user_id'] ?? '', ENT_QUOTES, 'UTF-8'); ?>" required>
  44. <div class="row">
  45. <div class="col-md-6">
  46. <div class="mb-3">
  47. <label for="Nname" class="form-label">Client Name <span class="text-danger">*</span></label>
  48. <input type="text" class="form-control form-control-sm" name="Nname" placeholder="Client Name" id="Nname" required maxlength="100">
  49. <div class="invalid-feedback">Please provide a client name.</div>
  50. </div>
  51. </div>
  52. <div class="col-md-6">
  53. <div class="mb-3">
  54. <label for="Ncompany" class="form-label">Company Name</label>
  55. <input type="text" class="form-control form-control-sm" name="Ncompany" placeholder="Company Name" id="Ncompany" maxlength="100">
  56. </div>
  57. </div>
  58. </div>
  59. <div class="row">
  60. <div class="col-md-6">
  61. <div class="mb-3">
  62. <label for="Nemail" class="form-label">Email Address <span class="text-danger">*</span></label>
  63. <input type="email" class="form-control form-control-sm" name="Nemail" placeholder="Email Address" id="Nemail" required maxlength="255">
  64. <div class="invalid-feedback">Please provide a valid email address.</div>
  65. </div>
  66. </div>
  67. <div class="col-md-6">
  68. <div class="mb-3">
  69. <label for="Nmobile" class="form-label">Mobile Number</label>
  70. <input type="tel" class="form-control form-control-sm" name="Nmobile" placeholder="Mobile Number" id="Nmobile" maxlength="20">
  71. </div>
  72. </div>
  73. </div>
  74. <hr>
  75. <div class="mb-3">
  76. <label for="Naddress" class="form-label">Address <span class="text-danger">*</span></label>
  77. <input type="text" class="form-control form-control-sm" name="Naddress" placeholder="Street Address" id="Naddress" required maxlength="255">
  78. <div class="invalid-feedback">Please provide an address.</div>
  79. </div>
  80. <div class="mb-3">
  81. <label for="Nstate" class="form-label">Town / State / Postcode <span class="text-danger">*</span></label>
  82. <input type="text" class="form-control form-control-sm" name="Nstate" placeholder="Town, State, Postcode" id="Nstate" required maxlength="255">
  83. <div class="invalid-feedback">Please provide town, state, and postcode.</div>
  84. <div class="form-text">Format: Town, State, Postcode (e.g., Sydney, NSW, 2000)</div>
  85. </div>
  86. </form>
  87. </div>
  88. <div class="modal-footer">
  89. <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
  90. <button form="newClientDetails" type="submit" name="NCsubmit" value="NCsubmit" class="btn btn-primary">
  91. <i class="fas fa-save"></i> Save Client
  92. </button>
  93. </div>
  94. </div>
  95. </div>
  96. </div>
  97. <script>
  98. // Bootstrap 5 form validation
  99. (function () {
  100. 'use strict'
  101. var forms = document.querySelectorAll('#newClientDetails')
  102. Array.prototype.slice.call(forms)
  103. .forEach(function (form) {
  104. form.addEventListener('submit', function (event) {
  105. if (!form.checkValidity()) {
  106. event.preventDefault()
  107. event.stopPropagation()
  108. } else {
  109. // Prevent default form submission and handle via AJAX
  110. event.preventDefault();
  111. submitNewClientForm(form);
  112. }
  113. form.classList.add('was-validated')
  114. }, false)
  115. })
  116. })()
  117. // AJAX form submission
  118. function submitNewClientForm(form) {
  119. const formData = new FormData(form);
  120. const submitBtn = form.querySelector('button[type="submit"]')
  121. || document.querySelector(`button[form="${form.id}"]`);
  122. if (!submitBtn) return;
  123. const originalText = submitBtn.innerHTML;
  124. // Disable button and show loading state
  125. submitBtn.disabled = true;
  126. submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Saving...';
  127. fetch(form.action, {
  128. method: 'POST',
  129. body: formData
  130. })
  131. .then(response => response.json())
  132. .then(data => {
  133. if (data.success) {
  134. // Success - add client to dropdown and close modal
  135. if (window.addClientToDropdown) {
  136. window.addClientToDropdown(data.client);
  137. }
  138. // Close modal
  139. const modal = bootstrap.Modal.getInstance(document.getElementById('newClient'));
  140. modal.hide();
  141. // Show success message
  142. showAlert('Client added successfully!', 'success');
  143. } else {
  144. // Error - show message
  145. showAlert(data.message || 'Error adding client', 'danger');
  146. }
  147. })
  148. .catch(error => {
  149. console.error('Error:', error);
  150. showAlert('Network error occurred. Please try again.', 'danger');
  151. })
  152. .finally(() => {
  153. // Re-enable button
  154. submitBtn.disabled = false;
  155. submitBtn.innerHTML = originalText;
  156. });
  157. }
  158. // Utility function to show alerts
  159. function showAlert(message, type) {
  160. // Remove existing alerts
  161. const existingAlerts = document.querySelectorAll('.alert');
  162. existingAlerts.forEach(alert => alert.remove());
  163. // Create new alert
  164. const alertDiv = document.createElement('div');
  165. alertDiv.className = `alert alert-${type} alert-dismissible fade show position-fixed`;
  166. alertDiv.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
  167. alertDiv.innerHTML = `
  168. ${message}
  169. <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
  170. `;
  171. document.body.appendChild(alertDiv);
  172. // Auto-remove after 5 seconds
  173. setTimeout(() => {
  174. if (alertDiv.parentNode) {
  175. alertDiv.remove();
  176. }
  177. }, 5000);
  178. }
  179. // Clear form when modal is hidden
  180. document.getElementById('newClient').addEventListener('hidden.bs.modal', function () {
  181. document.getElementById('newClientDetails').reset();
  182. document.getElementById('newClientDetails').classList.remove('was-validated');
  183. });
  184. </script>