From 58e8ae0759ccfa97c478ccae4e0778bdce70966f Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Thu, 22 Jan 2026 22:40:02 +0000
Subject: [PATCH] =DirectoryManager.php updates, some javascript tweaks for CRUD.js, and minor style changes

---
 inc/rest/routes/FormRoutes.php |  170 ++++++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 132 insertions(+), 38 deletions(-)

diff --git a/inc/rest/routes/FormRoutes.php b/inc/rest/routes/FormRoutes.php
index 1201a8e..c5c0d36 100644
--- a/inc/rest/routes/FormRoutes.php
+++ b/inc/rest/routes/FormRoutes.php
@@ -142,7 +142,6 @@
 	{
 		// Get form configuration
 		$form_config = FormBlock::getForm($form_type);
-
 		if (!$form_config) {
 			return new WP_Error('invalid_form', 'Form configuration not found.');
 		}
@@ -178,7 +177,6 @@
 		try {
 			$email_sent = $this->sendEmailNotification($form_type, $form_config, $processed_data, $files);
 		} catch (\Exception $e) {
-			;
 			return new WP_Error('email_failed', 'Email error: ' . $e->getMessage());
 		}
 
@@ -226,7 +224,7 @@
 			$value = $normalized_form_data[$field_name] ?? '';
 
 			if (in_array($field_config['type'], ['checkbox', 'set'])) {
-				$value = jvbCommaList((is_array($value)) ? $value : explode(',',$value));
+				$value = is_array($value) ? implode(',', $value) : $value;
 			}
 
 			// Check required fields
@@ -267,9 +265,6 @@
 	/**
 	 * Send email notification
 	 */
-	/**
-	 * Send email notification
-	 */
 	protected function sendEmailNotification(string $form_type, array $form_config, array $form_data, array $files = []): bool
 	{
 		$admin_email = apply_filters('jvb_form_email_to', $form_config['email_to'], $form_type, $form_data);
@@ -286,6 +281,17 @@
 			$submitter_name = $form_data['name'];
 		}
 
+		if (!array_key_exists('preheader', $form_config)) {
+			$preheader = $form_config['preheader'];
+		} else {
+			$preheader = sprintf(
+				'New %s submission from %s',
+				$form_config['title'],
+				$submitter_name ?: ($submitter_name ?: 'website visitor')
+			);
+		}
+
+
 		// Email headers
 		$headers = [];
 
@@ -319,23 +325,36 @@
 			);
 		}
 
-		// Build form data array for table
+		// Build form data array for table - using similar logic to JS
+		$skip_fields = array_merge(
+			['sendAll', 'action', 'form_id', 'form_type', 'timestamp', '_wpnonce', '_wp_http_referer', 'cf-turnstile-response'],
+			$form_config['ignore'] ?? []
+		);
+
 		$form = [];
 		foreach ($form_config['fields'] as $field_name => $config) {
-			// Skip file upload fields
-			if ($config['type'] == 'upload') {
+			// Skip ignored fields
+			if (in_array($field_name, $skip_fields)) {
+				continue;
+			}
+
+			// Skip upload fields (handled separately)
+			if ($config['type'] === 'upload') {
 				continue;
 			}
 
 			$value = $form_data[$field_name] ?? '';
 
 			// Skip empty fields
-			if (empty($value)) {
+			if ($this->isEmptyValue($value)) {
 				continue;
 			}
 
+			// Get label - check for summaryTitle first, then label (similar to JS checking legend then label)
+			$label = $config['summaryTitle'] ?? $config['label'] ?? ucfirst(str_replace('_', ' ', $field_name));
+
 			$form[] = [
-				'label' => $config['summaryTitle'] ?? $config['label'],
+				'label' => $label,
 				'value' => $this->formatFieldValueForEmail($field_name, $value, $config)
 			];
 		}
@@ -359,17 +378,34 @@
 			$subject,
 			$body,
 			'',
+			$preheader,
 			$headers,
 			$attachments
 		);
 	}
 
 	/**
+	 * Check if value is empty (similar to JS isEmptyValue)
+	 */
+	protected function isEmptyValue($value): bool
+	{
+		if ($value === null || $value === '' || $value === false) {
+			return true;
+		}
+
+		if (is_array($value) && empty($value)) {
+			return true;
+		}
+
+		return false;
+	}
+
+	/**
 	 * Format field value for email display
 	 */
 	protected function formatFieldValueForEmail(string $field_name, mixed $value, array $field_config): string
 	{
-		if (empty($value)) {
+		if ($this->isEmptyValue($value)) {
 			return '';
 		}
 
@@ -377,9 +413,17 @@
 		$type = $field_config['subType'] ?? $type;
 
 		switch ($type) {
+			case 'repeater':
+				return $this->formatRepeaterForEmail($value, $field_config);
+
+			case 'tag-list':
+				return $this->formatTagListForEmail($value);
+
+			case 'location':
+				return $this->formatLocationForEmail($value);
+
 			case 'phone':
 			case 'tel':
-				// Format phone number as xxx-xxx-xxxx
 				$cleaned = preg_replace('/\D/', '', $value);
 				if (strlen($cleaned) === 10) {
 					return substr($cleaned, 0, 3) . '-' . substr($cleaned, 3, 3) . '-' . substr($cleaned, 6);
@@ -388,51 +432,101 @@
 
 			case 'checkbox':
 			case 'set':
-				// Convert array of values to comma-separated labels
-				if (!is_array($value)) {
-					$value = explode(',', $value);
-				}
-				$labels = [];
-				foreach ($value as $val) {
+				$values = explode(',', $value);
+				$labels = array_map(function($val) use ($field_config) {
 					$val = trim($val);
-					if (isset($field_config['options'][$val])) {
-						$labels[] = $field_config['options'][$val];
-					} else {
-						$labels[] = $val; // Fallback to value if no label found
-					}
-				}
+					return $field_config['options'][$val] ?? ucfirst(str_replace('_', ' ', $val));
+				}, $values);
 				return implode(', ', $labels);
 
 			case 'radio':
 			case 'select':
-				// Return label instead of value
 				if (isset($field_config['options'][$value])) {
 					return $field_config['options'][$value];
 				}
-				return $value;
-
-			case 'textarea':
-			case 'wysiwyg':
-				// Keep line breaks for email display
-				return $value; // nl2br is already applied in the email body
+				// Fallback: capitalize the value
+				return ucfirst(str_replace('_', ' ', $value));
 
 			case 'true_false':
 				$true_text = $field_config['true_text'] ?? 'Yes';
 				$false_text = $field_config['false_text'] ?? 'No';
 				return ($value === '1' || $value === 1 || $value === true) ? $true_text : $false_text;
 
-			case 'email':
-				return $value; // Will be clickable in email client
-
-			case 'url':
-				return $value; // Will be clickable in email client
-
 			default:
 				return is_array($value) ? implode(', ', $value) : $value;
 		}
 	}
 
 	/**
+	 * Format repeater field for email
+	 */
+	protected function formatRepeaterForEmail(array $rows, array $field_config): string
+	{
+		$output = '';
+
+		foreach ($rows as $index => $row) {
+			$output .= '<strong>Entry ' . ($index + 1) . ':</strong><br>';
+
+			foreach ($row as $sub_field => $sub_value) {
+				if ($this->isEmptyValue($sub_value)) {
+					continue;
+				}
+
+				// Get sub-field label if available
+				$sub_config = $field_config['sub_fields'][$sub_field] ?? [];
+				$label = $sub_config['label'] ?? ucfirst(str_replace('_', ' ', $sub_field));
+
+				$output .= '&nbsp;&nbsp;' . esc_html($label) . ': ' . esc_html($sub_value) . '<br>';
+			}
+
+			$output .= '<br>';
+		}
+
+		return $output;
+	}
+
+	/**
+	 * Format tag-list field for email
+	 */
+	protected function formatTagListForEmail(array $tags): string
+	{
+		$items = [];
+
+		foreach ($tags as $tag) {
+			// Get first non-empty value as display
+			$display = '';
+			foreach ($tag as $field => $value) {
+				if (!$this->isEmptyValue($value)) {
+					$display = $value;
+					break;
+				}
+			}
+
+			if ($display) {
+				$items[] = $display;
+			}
+		}
+
+		return implode(', ', $items);
+	}
+
+	/**
+	 * Format location field for email
+	 */
+	protected function formatLocationForEmail(array $location): string
+	{
+		$parts = [];
+
+		if (!empty($location['street'])) $parts[] = $location['street'];
+		if (!empty($location['city'])) $parts[] = $location['city'];
+		if (!empty($location['province'])) $parts[] = $location['province'];
+		if (!empty($location['postal_code'])) $parts[] = $location['postal_code'];
+		if (!empty($location['country'])) $parts[] = $location['country'];
+
+		return !empty($parts) ? implode(', ', $parts) : ($location['address'] ?? '');
+	}
+
+	/**
 	 * Process uploaded files for email attachments
 	 * Files are in PHP's tmp directory and will be automatically cleaned up after request
 	 */

--
Gitblit v1.10.0