feat: Add trainer profile editing functionality

- Add profile edit form template
- Implement profile update handler with validation
- Add profile update shortcode registration
- Create E2E test for profile editing
- Support updating personal & business info, location info and training preferences
- Add password change functionality with validation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
bengizmo 2025-05-20 09:47:22 -03:00
parent 60369a212e
commit 20a5be3e65
4 changed files with 1241 additions and 0 deletions

View file

@ -0,0 +1,162 @@
/**
* @fileoverview E2E Tests for trainer profile editing functionality.
* This test suite verifies that trainers can view, edit, and update their profiles.
*/
import { test, expect } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';
import { DashboardPage } from './pages/DashboardPage';
// Test data
const TEST_USER = {
username: process.env.TEST_TRAINER_USERNAME || 'test_trainer',
password: process.env.TEST_TRAINER_PASSWORD || 'Test_password123'
};
const UPDATED_INFO = {
firstName: 'Updated',
lastName: 'Trainer',
displayName: 'Updated Trainer Profile',
bio: 'This is an updated bio from the automated test.',
businessName: 'Updated Training Co.',
businessPhone: '555-123-4567',
businessEmail: 'updated@example.com',
businessDesc: 'This is an updated business description from the automated test.'
};
test.describe('Trainer Profile Editing @trainer-profile', () => {
test.beforeEach(async ({ page }) => {
// Login before each test
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login(TEST_USER.username, TEST_USER.password);
// Verify we're logged in by checking if we're on the dashboard
const dashboardPage = new DashboardPage(page);
await expect(page).toHaveURL(/.*hvac-dashboard.*/);
await expect(dashboardPage.welcomeMessage).toBeVisible();
});
test('should navigate to edit profile page', async ({ page }) => {
// Navigate to trainer profile page first
await page.goto('/trainer-profile/');
await expect(page).toHaveURL(/.*trainer-profile.*/);
await expect(page.locator('h1')).toContainText('Trainer Profile');
// Click the edit profile button
await page.click('text=Edit Profile');
// Verify we're on the edit profile page
await expect(page).toHaveURL(/.*edit-profile.*/);
await expect(page.locator('h1')).toContainText('Edit Trainer Profile');
// Verify form is loaded with user's current info
await expect(page.locator('#first_name')).toBeVisible();
await expect(page.locator('#last_name')).toBeVisible();
await expect(page.locator('#display_name')).toBeVisible();
});
test('should update basic profile information', async ({ page }) => {
// Go directly to edit profile page
await page.goto('/edit-profile/');
// Clear and update form fields
await page.locator('#first_name').fill(UPDATED_INFO.firstName);
await page.locator('#last_name').fill(UPDATED_INFO.lastName);
await page.locator('#display_name').fill(UPDATED_INFO.displayName);
await page.locator('#description').fill(UPDATED_INFO.bio);
// Submit the form
await page.click('button[name="hvac_update_profile"]');
// Verify success message
await expect(page.locator('.hvac-message-success')).toBeVisible();
await expect(page.locator('.hvac-message-success')).toContainText('Your profile has been updated successfully');
// Navigate to profile page to verify changes
await page.goto('/trainer-profile/');
// Verify updated information is displayed
await expect(page.locator('.trainer-name')).toContainText(UPDATED_INFO.displayName);
await expect(page.locator('.trainer-bio')).toContainText(UPDATED_INFO.bio);
});
test('should update business information', async ({ page }) => {
// Go to edit profile page
await page.goto('/edit-profile/');
// Update business information
await page.locator('#business_name').fill(UPDATED_INFO.businessName);
await page.locator('#business_phone').fill(UPDATED_INFO.businessPhone);
await page.locator('#business_email').fill(UPDATED_INFO.businessEmail);
await page.locator('#business_description').fill(UPDATED_INFO.businessDesc);
// Submit the form
await page.click('button[name="hvac_update_profile"]');
// Verify success message
await expect(page.locator('.hvac-message-success')).toBeVisible();
// Navigate to profile page to verify changes
await page.goto('/trainer-profile/');
// Verify updated business information is displayed
await expect(page.locator('.business-name')).toContainText(UPDATED_INFO.businessName);
await expect(page.locator('.business-info')).toContainText(UPDATED_INFO.businessPhone);
await expect(page.locator('.business-info')).toContainText(UPDATED_INFO.businessEmail);
await expect(page.locator('.business-description')).toContainText(UPDATED_INFO.businessDesc);
});
test('should display appropriate validation errors', async ({ page }) => {
// Go to edit profile page
await page.goto('/edit-profile/');
// Clear required fields
await page.locator('#first_name').fill('');
await page.locator('#last_name').fill('');
await page.locator('#user_email').fill('');
// Submit the form
await page.click('button[name="hvac_update_profile"]');
// Verify validation errors
await expect(page.locator('#first_name_error')).toBeVisible();
await expect(page.locator('#last_name_error')).toBeVisible();
await expect(page.locator('#user_email_error')).toBeVisible();
// Invalid email format
await page.locator('#user_email').fill('not-an-email');
await page.click('button[name="hvac_update_profile"]');
await expect(page.locator('#user_email_error')).toBeVisible();
await expect(page.locator('#user_email_error')).toContainText('valid email');
});
test('should validate password change fields', async ({ page }) => {
// Go to edit profile page
await page.goto('/edit-profile/');
// Enter incorrect current password
await page.locator('#current_password').fill('wrong-password');
await page.locator('#new_password').fill('NewPassword123');
await page.locator('#confirm_new_password').fill('NewPassword123');
// Submit the form
await page.click('button[name="hvac_update_profile"]');
// Verify error message for incorrect current password
await expect(page.locator('#current_password_error')).toBeVisible();
await expect(page.locator('#current_password_error')).toContainText('incorrect');
// Test password mismatch
await page.locator('#current_password').fill(TEST_USER.password);
await page.locator('#new_password').fill('NewPassword123');
await page.locator('#confirm_new_password').fill('DifferentPassword123');
// Submit the form
await page.click('button[name="hvac_update_profile"]');
// Verify error message for password mismatch
await expect(page.locator('#confirm_new_password_error')).toBeVisible();
await expect(page.locator('#confirm_new_password_error')).toContainText('do not match');
});
});

View file

@ -231,6 +231,9 @@ class HVAC_Community_Events {
// Add trainer profile shortcode // Add trainer profile shortcode
add_shortcode('hvac_trainer_profile', array($this, 'render_trainer_profile')); add_shortcode('hvac_trainer_profile', array($this, 'render_trainer_profile'));
// Add edit profile shortcode
add_shortcode('hvac_edit_profile', array('HVAC_Registration', 'render_edit_profile_form'));
// Remove the event form shortcode as we're using TEC's shortcode instead // Remove the event form shortcode as we're using TEC's shortcode instead
// add_shortcode('hvac_event_form', array('HVAC_Community_Event_Handler', 'render_event_form')); // add_shortcode('hvac_event_form', array('HVAC_Community_Event_Handler', 'render_event_form'));
@ -343,6 +346,14 @@ class HVAC_Community_Events {
return $custom_template; return $custom_template;
} }
} }
// Check for edit-profile page
if (is_page('edit-profile')) {
$custom_template = HVAC_CE_PLUGIN_DIR . 'templates/template-edit-profile.php';
if (file_exists($custom_template)) {
return $custom_template;
}
}
// Check for single event view (temporary) // Check for single event view (temporary)
if ( is_singular( 'tribe_events' ) ) { if ( is_singular( 'tribe_events' ) ) {

View file

@ -10,7 +10,9 @@ if (!defined('ABSPATH')) {
class HVAC_Registration { class HVAC_Registration {
const TRANSIENT_PREFIX = 'hvac_reg_'; // Prefix for transients const TRANSIENT_PREFIX = 'hvac_reg_'; // Prefix for transients
const PROFILE_TRANSIENT_PREFIX = 'hvac_prof_'; // Prefix for profile transients
const REGISTRATION_ACTION = 'hvac_register'; // Action name for admin-post const REGISTRATION_ACTION = 'hvac_register'; // Action name for admin-post
const PROFILE_UPDATE_ACTION = 'hvac_update_profile'; // Action name for profile update
/** /**
* Constructor * Constructor
@ -18,6 +20,9 @@ class HVAC_Registration {
public function __construct() { public function __construct() {
// Register shortcode for registration form // Register shortcode for registration form
add_shortcode('hvac_trainer_registration', array($this, 'render_registration_form')); add_shortcode('hvac_trainer_registration', array($this, 'render_registration_form'));
// Register shortcode for edit profile form
add_shortcode('hvac_edit_profile', array($this, 'render_edit_profile_form'));
// Enqueue styles and scripts // Enqueue styles and scripts
add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts')); add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
@ -25,6 +30,9 @@ class HVAC_Registration {
// Hook the processing function to admin-post actions // Hook the processing function to admin-post actions
add_action('admin_post_nopriv_' . self::REGISTRATION_ACTION, array($this, 'process_registration_submission')); add_action('admin_post_nopriv_' . self::REGISTRATION_ACTION, array($this, 'process_registration_submission'));
add_action('admin_post_' . self::REGISTRATION_ACTION, array($this, 'process_registration_submission')); // Allow logged-in users? Maybe not needed but harmless. add_action('admin_post_' . self::REGISTRATION_ACTION, array($this, 'process_registration_submission')); // Allow logged-in users? Maybe not needed but harmless.
// Hook the profile update function to admin-post actions (only for logged in users)
add_action('admin_post_' . self::PROFILE_UPDATE_ACTION, array($this, 'process_profile_update'));
} }
/** /**
@ -1016,6 +1024,339 @@ class HVAC_Registration {
// TODO: Add send_user_approved_notification() // TODO: Add send_user_approved_notification()
// TODO: Add send_user_denied_notification() // TODO: Add send_user_denied_notification()
/**
* Renders the edit profile form shortcode output
*/
public function render_edit_profile_form() {
if (!is_user_logged_in()) {
return '<p>Please log in to edit your profile.</p>';
}
// We don't need to do anything here since the template file already handles the form rendering
// Just set up the shortcode to point to the template
ob_start();
include HVAC_CE_PLUGIN_DIR . 'templates/template-edit-profile.php';
return ob_get_clean();
}
/**
* Process profile update submission from the edit profile form
*/
public function process_profile_update() {
// Only logged-in users can update profiles
if (!is_user_logged_in()) {
wp_redirect(home_url('/community-login/'));
exit;
}
$user_id = get_current_user_id();
$user = get_userdata($user_id);
$errors = [];
$submitted_data = $_POST;
$profile_page_url = home_url('/edit-profile/');
// Verify nonce
if (!isset($_POST['hvac_profile_nonce']) || !wp_verify_nonce($_POST['hvac_profile_nonce'], 'hvac_update_profile')) {
$errors['nonce'] = 'Security check failed. Please try submitting the form again.';
error_log('[HVAC PROFILE DEBUG] Nonce check failed in profile update.');
$this->redirect_with_profile_errors($errors, $profile_page_url);
// No need for return/exit here, redirect_with_errors exits.
}
error_log('[HVAC PROFILE DEBUG] Nonce check passed in profile update.');
// File Upload Handling
$profile_image_data = null;
if (isset($_FILES['profile_image']) && $_FILES['profile_image']['error'] !== UPLOAD_ERR_NO_FILE) {
if ($_FILES['profile_image']['error'] === UPLOAD_ERR_OK) {
// Check if it's actually an uploaded file
if (!is_uploaded_file($_FILES['profile_image']['tmp_name'])) {
$errors['profile_image'] = 'File upload error (invalid temp file).';
error_log('[HVAC PROFILE DEBUG] Profile image upload error: Not an uploaded file.');
} else {
$allowed_types = ['image/jpeg', 'image/png', 'image/gif'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $_FILES['profile_image']['tmp_name']);
finfo_close($finfo);
if (!in_array($mime_type, $allowed_types)) {
$errors['profile_image'] = 'Invalid file type detected (' . esc_html($mime_type) . '). Please upload a JPG, PNG, or GIF.';
error_log('[HVAC PROFILE DEBUG] Profile image upload error: Invalid MIME type - ' . $mime_type);
} else {
$profile_image_data = $_FILES['profile_image']; // Store the whole $_FILES entry
error_log('[HVAC PROFILE DEBUG] Profile image seems valid.');
}
}
} else {
$errors['profile_image'] = 'There was an error uploading the profile image. Code: ' . $_FILES['profile_image']['error'];
error_log('[HVAC PROFILE DEBUG] Profile image upload error code: ' . $_FILES['profile_image']['error']);
}
}
// Validate form data
$validation_errors = $this->validate_profile_update($submitted_data, $user);
$errors = array_merge($errors, $validation_errors);
// Process if no errors
if (empty($errors)) {
error_log('[HVAC PROFILE DEBUG] Validation passed in profile update.');
$update_success = $this->update_user_profile($user_id, $submitted_data, $profile_image_data);
if (is_wp_error($update_success)) {
$errors['account'] = $update_success->get_error_message();
error_log('[HVAC PROFILE DEBUG] Profile update WP_Error: ' . $update_success->get_error_message());
$this->redirect_with_profile_errors($errors, $profile_page_url);
} elseif ($update_success) {
error_log('[HVAC PROFILE DEBUG] Profile update SUCCESS for user ID: ' . $user_id);
// Redirect to the profile page with success message
wp_safe_redirect(add_query_arg('updated', '1', $profile_page_url));
exit;
} else {
$errors['account'] = 'An unknown error occurred during profile update. Please try again.';
error_log('[HVAC PROFILE DEBUG] Profile update failed silently (returned false).');
$this->redirect_with_profile_errors($errors, $profile_page_url);
}
} else {
error_log('[HVAC PROFILE DEBUG] Validation errors found in profile update: ' . print_r($errors, true));
$this->redirect_with_profile_errors($errors, $profile_page_url);
}
}
/**
* Helper function to store profile errors in transient and redirect back to the form page.
*
* @param array $errors Array of error messages.
* @param string $redirect_url The URL to redirect back to.
*/
private function redirect_with_profile_errors($errors, $redirect_url) {
$transient_id = uniqid(); // Generate unique ID for transient key
$transient_key = self::PROFILE_TRANSIENT_PREFIX . $transient_id;
$transient_data = [
'errors' => $errors,
];
// Store for 5 minutes
set_transient($transient_key, $transient_data, MINUTE_IN_SECONDS * 5);
// Add query arguments to the redirect URL
$redirect_url = add_query_arg([
'prof_error' => '1',
'tid' => $transient_id,
], $redirect_url);
error_log('[HVAC PROFILE DEBUG] Redirecting back with errors. URL: ' . $redirect_url . ' Transient Key: ' . $transient_key);
wp_safe_redirect($redirect_url);
exit; // Stop execution after redirect
}
/**
* Validate profile update data
*
* @param array $data Submitted form data ($_POST).
* @param WP_User $user Current user object.
* @return array Array of errors, empty if valid.
*/
public function validate_profile_update($data, $user) {
error_log('[HVAC PROFILE DEBUG] validate_profile_update: Validating data');
$errors = array();
// Required field validation
$required_fields = [
'first_name' => 'First Name',
'last_name' => 'Last Name',
'display_name' => 'Display Name',
'user_email' => 'Email',
'description' => 'Biographical Info',
'business_name' => 'Business Name',
'business_phone' => 'Business Phone',
'business_email' => 'Business Email',
'business_description' => 'Business Description',
'user_country' => 'Country',
'user_state' => 'State/Province',
'user_city' => 'City',
'user_zip' => 'Zip/Postal Code',
'business_type' => 'Business Type',
];
foreach ($required_fields as $field => $label) {
// Use trim to catch spaces-only input
if (empty($data[$field]) || trim($data[$field]) === '') {
$errors[$field] = $label . ' is required.';
}
}
// Required checkbox groups
$required_checkboxes = [
'training_audience' => 'Training Audience',
'training_formats' => 'Training Formats',
'training_locations' => 'Training Locations',
'training_resources' => 'Training Resources',
];
foreach ($required_checkboxes as $field => $label) {
// Check if the key exists and is a non-empty array
if (empty($data[$field]) || !is_array($data[$field])) {
$errors[$field] = 'Please select at least one option for ' . $label . '.';
}
}
// Email validation
if (!empty($data['user_email']) && !is_email($data['user_email'])) {
$errors['user_email'] = 'Please enter a valid email address.';
}
if (!empty($data['business_email']) && !is_email($data['business_email'])) {
$errors['business_email'] = 'Please enter a valid business email address.';
}
// Email exists validation (only if email is changed and valid)
if (empty($errors['user_email']) && !empty($data['user_email']) && $data['user_email'] !== $user->user_email && email_exists($data['user_email'])) {
$errors['user_email'] = 'This email address is already registered to another account.';
}
// URL validation (optional fields)
if (!empty($data['user_url']) && !filter_var($data['user_url'], FILTER_VALIDATE_URL)) {
$errors['user_url'] = 'Please enter a valid URL for your personal website.';
}
if (!empty($data['user_linkedin']) && !filter_var($data['user_linkedin'], FILTER_VALIDATE_URL)) {
$errors['user_linkedin'] = 'Please enter a valid URL for your LinkedIn profile.';
}
if (!empty($data['business_website']) && !filter_var($data['business_website'], FILTER_VALIDATE_URL)) {
$errors['business_website'] = 'Please enter a valid URL for your business website.';
}
// State/Province 'Other' validation
if (!empty($data['user_country'])) {
if ($data['user_country'] !== 'United States' && $data['user_country'] !== 'Canada') {
// If country is not US/CA, state *must* be 'Other'
if (empty($data['user_state']) || $data['user_state'] !== 'Other') {
$errors['user_state'] = 'Please select "Other" for State/Province if your country is not US or Canada.';
} elseif (empty($data['user_state_other']) || trim($data['user_state_other']) === '') {
// If state is 'Other', the text input must not be empty
$errors['user_state_other'] = 'Please enter your state/province.';
}
} elseif (!empty($data['user_state'])) {
// If country is US/CA
if ($data['user_state'] === 'Other') {
// State cannot be 'Other' if country is US/CA
$errors['user_state'] = 'Please select your state/province from the list.';
}
}
}
// Password validation (only if user is attempting to change password)
if (!empty($data['current_password']) || !empty($data['new_password']) || !empty($data['confirm_new_password'])) {
// Verify current password
if (empty($data['current_password'])) {
$errors['current_password'] = 'Please enter your current password.';
} elseif (!wp_check_password($data['current_password'], $user->user_pass, $user->ID)) {
$errors['current_password'] = 'Current password is incorrect.';
}
// Validate new password
if (empty($data['new_password'])) {
$errors['new_password'] = 'Please enter a new password.';
} elseif (strlen($data['new_password']) < 8) {
$errors['new_password'] = 'Password must be at least 8 characters long.';
} elseif (!preg_match('/[A-Z]/', $data['new_password'])) {
$errors['new_password'] = 'Password must contain at least one uppercase letter.';
} elseif (!preg_match('/[a-z]/', $data['new_password'])) {
$errors['new_password'] = 'Password must contain at least one lowercase letter.';
} elseif (!preg_match('/[0-9]/', $data['new_password'])) {
$errors['new_password'] = 'Password must contain at least one number.';
}
// Confirm new password
if (empty($data['confirm_new_password'])) {
$errors['confirm_new_password'] = 'Please confirm your new password.';
} elseif ($data['new_password'] !== $data['confirm_new_password']) {
$errors['confirm_new_password'] = 'New passwords do not match.';
}
}
error_log('[HVAC PROFILE DEBUG] validate_profile_update: Validation result - ' . (empty($errors) ? 'VALID' : 'INVALID'));
return $errors;
}
/**
* Update the user profile with submitted data
*
* @param int $user_id The user ID to update.
* @param array $data The submitted form data.
* @param array|null $profile_image_data The profile image file data, if any.
* @return bool|WP_Error True on success, WP_Error on failure.
*/
private function update_user_profile($user_id, $data, $profile_image_data = null) {
// Sanitize and prepare user data for update
$userdata = array(
'ID' => $user_id,
'first_name' => sanitize_text_field($data['first_name']),
'last_name' => sanitize_text_field($data['last_name']),
'display_name' => sanitize_text_field($data['display_name']),
'user_email' => sanitize_email($data['user_email']),
'user_url' => !empty($data['user_url']) ? esc_url_raw($data['user_url']) : '',
'description' => wp_kses_post($data['description']),
);
// Handle password update if provided
if (!empty($data['new_password']) && !empty($data['current_password']) && !empty($data['confirm_new_password'])) {
$userdata['user_pass'] = $data['new_password']; // wp_update_user will hash the password
}
// Update user data
$update_result = wp_update_user($userdata);
if (is_wp_error($update_result)) {
error_log('[HVAC PROFILE DEBUG] wp_update_user failed: ' . $update_result->get_error_message());
return $update_result;
}
// Update user meta
$meta_fields = [
'user_linkedin' => !empty($data['user_linkedin']) ? esc_url_raw($data['user_linkedin']) : '',
'personal_accreditation' => !empty($data['personal_accreditation']) ? sanitize_text_field($data['personal_accreditation']) : '',
'business_name' => sanitize_text_field($data['business_name']),
'business_phone' => sanitize_text_field($data['business_phone']),
'business_email' => sanitize_email($data['business_email']),
'business_website' => !empty($data['business_website']) ? esc_url_raw($data['business_website']) : '',
'business_description' => wp_kses_post($data['business_description']),
'user_country' => sanitize_text_field($data['user_country']),
'user_state' => ($data['user_state'] === 'Other' && isset($data['user_state_other'])) ? sanitize_text_field($data['user_state_other']) : sanitize_text_field($data['user_state']),
'user_city' => sanitize_text_field($data['user_city']),
'user_zip' => sanitize_text_field($data['user_zip']),
'business_type' => sanitize_text_field($data['business_type']),
'training_audience' => (!empty($data['training_audience']) && is_array($data['training_audience'])) ? array_map('sanitize_text_field', $data['training_audience']) : [],
'training_formats' => (!empty($data['training_formats']) && is_array($data['training_formats'])) ? array_map('sanitize_text_field', $data['training_formats']) : [],
'training_locations' => (!empty($data['training_locations']) && is_array($data['training_locations'])) ? array_map('sanitize_text_field', $data['training_locations']) : [],
'training_resources' => (!empty($data['training_resources']) && is_array($data['training_resources'])) ? array_map('sanitize_text_field', $data['training_resources']) : [],
'annual_revenue_target' => !empty($data['annual_revenue_target']) ? intval($data['annual_revenue_target']) : '',
];
foreach ($meta_fields as $key => $value) {
update_user_meta($user_id, $key, $value);
}
error_log('[HVAC PROFILE DEBUG] User meta updated for user ID: ' . $user_id);
// Handle profile image upload if provided
if ($profile_image_data) {
error_log('[HVAC PROFILE DEBUG] Attempting profile image upload for user ID: ' . $user_id);
// We don't need the return value here unless we want to report specific upload errors
$this->handle_profile_image_upload($user_id, $profile_image_data);
}
// Update organizer profile if it exists
$organizer_id = get_user_meta($user_id, 'hvac_organizer_id', true);
if ($organizer_id && get_post_type($organizer_id) === Tribe__Events__Main::ORGANIZER_POST_TYPE) {
error_log('[HVAC PROFILE DEBUG] Updating organizer profile for user ID: ' . $user_id);
$this->create_organizer_profile($user_id, $meta_fields);
}
// Update venue profile if it exists
$venue_id = get_user_meta($user_id, 'hvac_venue_id', true);
if ($venue_id && get_post_type($venue_id) === Tribe__Events__Main::VENUE_POST_TYPE) {
error_log('[HVAC PROFILE DEBUG] Updating venue profile for user ID: ' . $user_id);
$this->create_training_venue($user_id, $meta_fields);
}
return true;
}
/** /**
* Get list of countries (simplified) * Get list of countries (simplified)

View file

@ -0,0 +1,727 @@
<?php
/**
* Template Name: HVAC Edit Trainer Profile
*
* This template handles the editing of the HVAC Trainer Profile.
* It allows users to update their personal information, business details, and training preferences.
*
* @package HVAC Community Events
* @subpackage Templates
* @author HVAC Community Events
* @version 1.0.0
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// --- Security Check & Data Loading ---
// Ensure user is logged in and has the correct role
if ( ! is_user_logged_in() || ! current_user_can( 'view_hvac_dashboard' ) ) {
// Redirect to login page
wp_safe_redirect( home_url( '/community-login/' ) );
exit;
}
// Get the current user ID and data
$user_id = get_current_user_id();
$user = get_userdata($user_id);
// Load user meta data
$user_meta = array(
'personal_accreditation' => get_user_meta($user_id, 'personal_accreditation', true),
'business_name' => get_user_meta($user_id, 'business_name', true),
'business_phone' => get_user_meta($user_id, 'business_phone', true),
'business_email' => get_user_meta($user_id, 'business_email', true),
'business_website' => get_user_meta($user_id, 'business_website', true),
'business_description' => get_user_meta($user_id, 'business_description', true),
'user_country' => get_user_meta($user_id, 'user_country', true),
'user_state' => get_user_meta($user_id, 'user_state', true),
'user_city' => get_user_meta($user_id, 'user_city', true),
'user_zip' => get_user_meta($user_id, 'user_zip', true),
'user_linkedin' => get_user_meta($user_id, 'user_linkedin', true),
'business_type' => get_user_meta($user_id, 'business_type', true),
'training_audience' => get_user_meta($user_id, 'training_audience', true),
'training_formats' => get_user_meta($user_id, 'training_formats', true),
'training_locations' => get_user_meta($user_id, 'training_locations', true),
'training_resources' => get_user_meta($user_id, 'training_resources', true),
'annual_revenue_target' => get_user_meta($user_id, 'annual_revenue_target', true),
);
// Get profile image
$profile_image_id = get_user_meta($user_id, 'profile_image_id', true);
$profile_image_url = '';
if ($profile_image_id) {
$profile_image_url = wp_get_attachment_url($profile_image_id);
}
// Get messages (success, error) from query parameters if present
$message = '';
$message_type = '';
if (isset($_GET['updated']) && $_GET['updated'] === '1') {
$message = 'Your profile has been updated successfully.';
$message_type = 'success';
} elseif (isset($_GET['updated']) && $_GET['updated'] === '0') {
$message = 'There was an error updating your profile. Please try again.';
$message_type = 'error';
}
// Check if we have form errors from a previous submission via transient
$errors = [];
$transient_key = null;
if (isset($_GET['prof_error']) && $_GET['prof_error'] === '1' && isset($_GET['tid'])) {
$transient_key = 'hvac_prof_' . sanitize_key($_GET['tid']);
$transient_data = get_transient($transient_key);
if ($transient_data && is_array($transient_data)) {
$errors = $transient_data['errors'] ?? [];
// Delete the transient immediately after retrieving
delete_transient($transient_key);
}
}
// Define country, state, and province options
function get_us_states() {
return array(
'AL' => 'Alabama', 'AK' => 'Alaska', 'AZ' => 'Arizona', 'AR' => 'Arkansas', 'CA' => 'California',
'CO' => 'Colorado', 'CT' => 'Connecticut', 'DE' => 'Delaware', 'DC' => 'District of Columbia', 'FL' => 'Florida',
'GA' => 'Georgia', 'HI' => 'Hawaii', 'ID' => 'Idaho', 'IL' => 'Illinois', 'IN' => 'Indiana',
'IA' => 'Iowa', 'KS' => 'Kansas', 'KY' => 'Kentucky', 'LA' => 'Louisiana', 'ME' => 'Maine',
'MD' => 'Maryland', 'MA' => 'Massachusetts', 'MI' => 'Michigan', 'MN' => 'Minnesota', 'MS' => 'Mississippi',
'MO' => 'Missouri', 'MT' => 'Montana', 'NE' => 'Nebraska', 'NV' => 'Nevada', 'NH' => 'New Hampshire',
'NJ' => 'New Jersey', 'NM' => 'New Mexico', 'NY' => 'New York', 'NC' => 'North Carolina', 'ND' => 'North Dakota',
'OH' => 'Ohio', 'OK' => 'Oklahoma', 'OR' => 'Oregon', 'PA' => 'Pennsylvania', 'RI' => 'Rhode Island',
'SC' => 'South Carolina', 'SD' => 'South Dakota', 'TN' => 'Tennessee', 'TX' => 'Texas', 'UT' => 'Utah',
'VT' => 'Vermont', 'VA' => 'Virginia', 'WA' => 'Washington', 'WV' => 'West Virginia', 'WI' => 'Wisconsin',
'WY' => 'Wyoming'
);
}
function get_canadian_provinces() {
return array(
'AB' => 'Alberta', 'BC' => 'British Columbia', 'MB' => 'Manitoba', 'NB' => 'New Brunswick',
'NL' => 'Newfoundland and Labrador', 'NS' => 'Nova Scotia', 'ON' => 'Ontario', 'PE' => 'Prince Edward Island',
'QC' => 'Quebec', 'SK' => 'Saskatchewan', 'NT' => 'Northwest Territories', 'NU' => 'Nunavut', 'YT' => 'Yukon'
);
}
function get_country_list() {
return array(
'US' => 'United States',
'CA' => 'Canada',
'GB' => 'United Kingdom',
'AU' => 'Australia',
'NZ' => 'New Zealand',
'DE' => 'Germany',
'FR' => 'France',
'IT' => 'Italy',
'ES' => 'Spain',
'JP' => 'Japan',
'CN' => 'China',
'IN' => 'India',
'BR' => 'Brazil',
'MX' => 'Mexico',
// Add more countries as needed
);
}
// --- Template Start ---
get_header(); // Use theme's header
?>
<div id="primary" class="content-area primary ast-container">
<main id="main" class="site-main">
<!-- Profile Header & Navigation -->
<div class="hvac-dashboard-header">
<h1 class="entry-title">Edit Trainer Profile</h1>
<div class="hvac-dashboard-nav">
<a href="<?php echo esc_url(home_url('/trainer-profile/')); ?>" class="ast-button ast-button-secondary">Back to Profile</a>
<a href="<?php echo esc_url(home_url('/hvac-dashboard/')); ?>" class="ast-button ast-button-primary">Dashboard</a>
<a href="<?php echo esc_url(wp_logout_url(home_url('/community-login/'))); ?>" class="ast-button ast-button-secondary">Logout</a>
</div>
</div>
<?php if ($message) : ?>
<div class="hvac-message hvac-message-<?php echo esc_attr($message_type); ?>">
<p><?php echo esc_html($message); ?></p>
</div>
<?php endif; ?>
<div class="hvac-edit-profile-container">
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" id="hvac-edit-profile-form" enctype="multipart/form-data" novalidate>
<input type="hidden" name="action" value="hvac_update_profile">
<?php wp_nonce_field('hvac_update_profile', 'hvac_profile_nonce'); ?>
<!-- Personal Information Section -->
<div class="form-section">
<h3>Personal Information</h3>
<div class="form-row form-row-half">
<div>
<label for="first_name"><strong>First Name *</strong></label>
<input type="text" name="first_name" id="first_name" value="<?php echo esc_attr($user->first_name); ?>" required aria-describedby="first_name_error">
<?php if (isset($errors['first_name'])) echo '<p class="error-message" id="first_name_error">' . esc_html($errors['first_name']) . '</p>'; ?>
</div>
<div>
<label for="last_name"><strong>Last Name *</strong></label>
<input type="text" name="last_name" id="last_name" value="<?php echo esc_attr($user->last_name); ?>" required aria-describedby="last_name_error">
<?php if (isset($errors['last_name'])) echo '<p class="error-message" id="last_name_error">' . esc_html($errors['last_name']) . '</p>'; ?>
</div>
</div>
<div class="form-row">
<label for="display_name"><strong>Display Name *</strong></label>
<input type="text" name="display_name" id="display_name" value="<?php echo esc_attr($user->display_name); ?>" required aria-describedby="display_name_hint display_name_error">
<small id="display_name_hint">This will be the name displayed to other users on the site.</small>
<?php if (isset($errors['display_name'])) echo '<p class="error-message" id="display_name_error">' . esc_html($errors['display_name']) . '</p>'; ?>
</div>
<div class="form-row form-row-half">
<div>
<label for="user_email"><strong>Email *</strong></label>
<input type="email" name="user_email" id="user_email" value="<?php echo esc_attr($user->user_email); ?>" required aria-describedby="user_email_error">
<?php if (isset($errors['user_email'])) echo '<p class="error-message" id="user_email_error">' . esc_html($errors['user_email']) . '</p>'; ?>
</div>
<div>
<label for="user_url">Personal Website (optional)</label>
<input type="url" name="user_url" id="user_url" value="<?php echo esc_attr($user->user_url); ?>" aria-describedby="user_url_error">
<?php if (isset($errors['user_url'])) echo '<p class="error-message" id="user_url_error">' . esc_html($errors['user_url']) . '</p>'; ?>
</div>
</div>
<div class="form-row">
<label for="user_linkedin">LinkedIn Profile URL (optional)</label>
<input type="url" name="user_linkedin" id="user_linkedin" value="<?php echo esc_attr($user_meta['user_linkedin']); ?>" aria-describedby="user_linkedin_error">
<?php if (isset($errors['user_linkedin'])) echo '<p class="error-message" id="user_linkedin_error">' . esc_html($errors['user_linkedin']) . '</p>'; ?>
</div>
<div class="form-row">
<label for="personal_accreditation">Personal Accreditation (optional)</label>
<input type="text" name="personal_accreditation" id="personal_accreditation" value="<?php echo esc_attr($user_meta['personal_accreditation']); ?>" aria-describedby="personal_accreditation_hint">
<small id="personal_accreditation_hint">Enter your abbreviated accreditations separated by commas.</small>
</div>
<div class="form-row">
<label for="description"><strong>Biographical Info *</strong></label>
<textarea name="description" id="description" rows="4" required aria-describedby="description_hint description_error"><?php echo esc_textarea($user->description); ?></textarea>
<small id="description_hint">A short bio about yourself. This will be displayed on your profile page.</small>
<?php if (isset($errors['description'])) echo '<p class="error-message" id="description_error">' . esc_html($errors['description']) . '</p>'; ?>
</div>
<div class="form-row">
<label for="profile_image">Profile Image</label>
<div class="profile-image-preview">
<?php if ($profile_image_url) : ?>
<img src="<?php echo esc_url($profile_image_url); ?>" alt="<?php echo esc_attr($user->display_name); ?>" width="150" height="150">
<?php else : ?>
<div class="hvac-profile-image-placeholder">
<span><?php echo esc_html(substr($user->first_name, 0, 1) . substr($user->last_name, 0, 1)); ?></span>
</div>
<?php endif; ?>
</div>
<div class="profile-image-upload">
<input type="file" name="profile_image" id="profile_image" accept="image/jpeg,image/png,image/gif" aria-describedby="profile_image_hint profile_image_error">
<small id="profile_image_hint">To update your profile image, select a new .jpg, .png, or .gif file. Leave empty to keep your current image.</small>
<?php if (isset($errors['profile_image'])) echo '<p class="error-message" id="profile_image_error">' . esc_html($errors['profile_image']) . '</p>'; ?>
</div>
</div>
</div>
<!-- Business Information Section -->
<div class="form-section">
<h3>Business Information</h3>
<div class="form-row">
<label for="business_name"><strong>Business Name *</strong></label>
<input type="text" name="business_name" id="business_name" value="<?php echo esc_attr($user_meta['business_name']); ?>" required aria-describedby="business_name_error">
<?php if (isset($errors['business_name'])) echo '<p class="error-message" id="business_name_error">' . esc_html($errors['business_name']) . '</p>'; ?>
</div>
<div class="form-row form-row-half">
<div>
<label for="business_phone"><strong>Business Phone *</strong></label>
<input type="tel" name="business_phone" id="business_phone" value="<?php echo esc_attr($user_meta['business_phone']); ?>" required aria-describedby="business_phone_error">
<?php if (isset($errors['business_phone'])) echo '<p class="error-message" id="business_phone_error">' . esc_html($errors['business_phone']) . '</p>'; ?>
</div>
<div>
<label for="business_email"><strong>Business Email *</strong></label>
<input type="email" name="business_email" id="business_email" value="<?php echo esc_attr($user_meta['business_email']); ?>" required aria-describedby="business_email_error">
<?php if (isset($errors['business_email'])) echo '<p class="error-message" id="business_email_error">' . esc_html($errors['business_email']) . '</p>'; ?>
</div>
</div>
<div class="form-row">
<label for="business_website">Business Website (optional)</label>
<input type="url" name="business_website" id="business_website" value="<?php echo esc_attr($user_meta['business_website']); ?>" aria-describedby="business_website_error">
<?php if (isset($errors['business_website'])) echo '<p class="error-message" id="business_website_error">' . esc_html($errors['business_website']) . '</p>'; ?>
</div>
<div class="form-row">
<label for="business_description"><strong>Business Description *</strong></label>
<textarea name="business_description" id="business_description" rows="4" required aria-describedby="business_description_error"><?php echo esc_textarea($user_meta['business_description']); ?></textarea>
<?php if (isset($errors['business_description'])) echo '<p class="error-message" id="business_description_error">' . esc_html($errors['business_description']) . '</p>'; ?>
</div>
</div>
<!-- Address Information Section -->
<div class="form-section">
<h3>Address Information</h3>
<div class="form-row">
<label for="user_country"><strong>Country *</strong></label>
<select name="user_country" id="user_country" required aria-describedby="user_country_error">
<option value="">Select Country</option>
<option value="United States" <?php selected($user_meta['user_country'], 'United States'); ?>>United States</option>
<option value="Canada" <?php selected($user_meta['user_country'], 'Canada'); ?>>Canada</option>
<option value="" disabled>---</option>
<?php
$countries = get_country_list();
foreach ($countries as $code => $name) {
if ($code !== 'US' && $code !== 'CA') {
echo '<option value="' . esc_attr($name) . '" ' . selected($user_meta['user_country'], $name, false) . '>' . esc_html($name) . '</option>';
}
}
?>
</select>
<?php if (isset($errors['user_country'])) echo '<p class="error-message" id="user_country_error">' . esc_html($errors['user_country']) . '</p>'; ?>
</div>
<div class="form-row form-row-half">
<div>
<label for="user_state"><strong>State/Province *</strong></label>
<select name="user_state" id="user_state" required aria-describedby="user_state_error">
<option value="">Select State/Province</option>
<option value="Other" <?php selected($user_meta['user_state'], 'Other'); ?>>Other</option>
<?php
// Pre-populate selected state if available
$selected_state = $user_meta['user_state'];
if (!empty($selected_state) && $selected_state !== 'Other') {
$us_states = get_us_states();
$ca_provinces = get_canadian_provinces();
$is_us_state = array_key_exists($selected_state, $us_states);
$is_ca_province = array_key_exists($selected_state, $ca_provinces);
if (!$is_us_state && !$is_ca_province) {
echo '<option value="' . esc_attr($selected_state) . '" selected>' . esc_html($selected_state) . '</option>';
}
}
?>
</select>
<input type="text" name="user_state_other" id="user_state_other"
value="<?php echo esc_attr($user_meta['user_state']); ?>"
style="<?php echo (($user_meta['user_state'] === 'Other' || (($user_meta['user_country'] !== 'United States' && $user_meta['user_country'] !== 'Canada')))) ? '' : 'display:none;'; ?> margin-top: 0.5rem;"
placeholder="Enter your state/province"
aria-describedby="user_state_other_error">
<?php if (isset($errors['user_state'])) echo '<p class="error-message" id="user_state_error">' . esc_html($errors['user_state']) . '</p>'; ?>
<?php if (isset($errors['user_state_other'])) echo '<p class="error-message" id="user_state_other_error">' . esc_html($errors['user_state_other']) . '</p>'; ?>
</div>
<div>
<label for="user_city"><strong>City *</strong></label>
<input type="text" name="user_city" id="user_city" value="<?php echo esc_attr($user_meta['user_city']); ?>" required aria-describedby="user_city_error">
<?php if (isset($errors['user_city'])) echo '<p class="error-message" id="user_city_error">' . esc_html($errors['user_city']) . '</p>'; ?>
</div>
</div>
<div class="form-row">
<label for="user_zip"><strong>Zip/Postal Code *</strong></label>
<input type="text" name="user_zip" id="user_zip" value="<?php echo esc_attr($user_meta['user_zip']); ?>" required aria-describedby="user_zip_error">
<?php if (isset($errors['user_zip'])) echo '<p class="error-message" id="user_zip_error">' . esc_html($errors['user_zip']) . '</p>'; ?>
</div>
</div>
<!-- Training Information Section -->
<div class="form-section">
<h3>Training Information</h3>
<div class="form-row">
<label id="business_type_label"><strong>Business Type *</strong></label>
<small>What type of business are you?</small>
<div class="radio-group" role="radiogroup" aria-labelledby="business_type_label">
<?php
$business_types = ["Manufacturer", "Distributor", "Contractor", "Consultant", "Educator", "Government", "Other"];
foreach ($business_types as $type) {
echo '<label><input type="radio" name="business_type" value="' . esc_attr($type) . '" ' . checked($user_meta['business_type'], $type, false) . ' required> ' . esc_html($type) . '</label>';
}
?>
</div>
<?php if (isset($errors['business_type'])) echo '<p class="error-message">' . esc_html($errors['business_type']) . '</p>'; ?>
</div>
<div class="form-row">
<label id="training_audience_label"><strong>Training Audience *</strong></label>
<small>Who do you offer training to? (Select all that apply)</small>
<div class="checkbox-group" role="group" aria-labelledby="training_audience_label">
<?php
$audience_options = [
"Anyone" => "Anyone (open to the public)",
"Industry professionals" => "Industry professionals",
"Internal staff" => "Internal staff in my company",
"Registered students" => "Registered students/members of my org/institution"
];
$selected_audience = $user_meta['training_audience'];
if (!is_array($selected_audience)) $selected_audience = []; // Ensure it's an array
foreach ($audience_options as $value => $label) {
echo '<label><input type="checkbox" name="training_audience[]" value="' . esc_attr($value) . '" ' . checked(in_array($value, $selected_audience), true, false) . '> ' . esc_html($label) . '</label>';
}
?>
</div>
<?php if (isset($errors['training_audience'])) echo '<p class="error-message">' . esc_html($errors['training_audience']) . '</p>'; ?>
</div>
<div class="form-row">
<label id="training_formats_label"><strong>Training Formats *</strong></label>
<small>What formats of training do you offer?</small>
<div class="checkbox-group" role="group" aria-labelledby="training_formats_label">
<?php
$format_options = ["In-person", "Virtual", "Hybrid", "On-demand"];
$selected_formats = $user_meta['training_formats'];
if (!is_array($selected_formats)) $selected_formats = []; // Ensure it's an array
foreach ($format_options as $format) {
echo '<label><input type="checkbox" name="training_formats[]" value="' . esc_attr($format) . '" ' . checked(in_array($format, $selected_formats), true, false) . '> ' . esc_html($format) . '</label>';
}
?>
</div>
<?php if (isset($errors['training_formats'])) echo '<p class="error-message">' . esc_html($errors['training_formats']) . '</p>'; ?>
</div>
<div class="form-row">
<label id="training_locations_label"><strong>Training Locations *</strong></label>
<small>Where are you willing to provide training? (Select all that apply)</small>
<div class="checkbox-group" role="group" aria-labelledby="training_locations_label">
<?php
$location_options = ["Online", "Local", "Regional Travel", "National Travel", "International Travel"];
$selected_locations = $user_meta['training_locations'];
if (!is_array($selected_locations)) $selected_locations = []; // Ensure it's an array
foreach ($location_options as $location) {
echo '<label><input type="checkbox" name="training_locations[]" value="' . esc_attr($location) . '" ' . checked(in_array($location, $selected_locations), true, false) . '> ' . esc_html($location) . '</label>';
}
?>
</div>
<?php if (isset($errors['training_locations'])) echo '<p class="error-message">' . esc_html($errors['training_locations']) . '</p>'; ?>
</div>
<div class="form-row">
<label id="training_resources_label"><strong>Training Resources *</strong></label>
<small>What training resources do you have access to? (Select all that apply)</small>
<div class="checkbox-group" role="group" aria-labelledby="training_resources_label">
<?php
$resource_options = ["Classroom", "Training Lab", "Ducted Furnace(s)", "Ducted Air Handler(s)", "Ducted Air Conditioner(s)", "Ducted Heat Pump(s)", "Ductless Heat Pump(s)", "Training Manuals", "Presentation Slides", "LMS Platform / SCORM Files", "Custom Curriculum", "Other"];
$selected_resources = $user_meta['training_resources'];
if (!is_array($selected_resources)) $selected_resources = []; // Ensure it's an array
foreach ($resource_options as $resource) {
echo '<label><input type="checkbox" name="training_resources[]" value="' . esc_attr($resource) . '" ' . checked(in_array($resource, $selected_resources), true, false) . '> ' . esc_html($resource) . '</label>';
}
?>
</div>
<?php if (isset($errors['training_resources'])) echo '<p class="error-message">' . esc_html($errors['training_resources']) . '</p>'; ?>
</div>
</div>
<!-- Revenue Information Section -->
<div class="form-section">
<h3>Revenue Information</h3>
<div class="form-row">
<label for="annual_revenue_target">Annual Revenue Target (optional)</label>
<small>It's our goal to help you generate revenue through your training. How much revenue are you looking to generate annually though your training on Upskill HVAC?</small>
<input type="number" name="annual_revenue_target" id="annual_revenue_target" min="0" step="1" value="<?php echo esc_attr($user_meta['annual_revenue_target']); ?>">
</div>
</div>
<!-- Password Change Section -->
<div class="form-section">
<h3>Change Password <span class="optional-section">(optional)</span></h3>
<p class="section-description">Leave these fields blank if you do not wish to change your password.</p>
<div class="form-row form-row-half">
<div>
<label for="current_password">Current Password</label>
<input type="password" name="current_password" id="current_password" aria-describedby="current_password_error">
<?php if (isset($errors['current_password'])) echo '<p class="error-message" id="current_password_error">' . esc_html($errors['current_password']) . '</p>'; ?>
</div>
<div>
<label for="new_password">New Password</label>
<input type="password" name="new_password" id="new_password" pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}" title="Password must be at least 8 characters long, and include at least one uppercase letter, one lowercase letter, and one number." aria-describedby="new_password_hint new_password_error">
<small id="new_password_hint">Password must be at least 8 characters long, and include at least one uppercase letter, one lowercase letter, and one number.</small>
<?php if (isset($errors['new_password'])) echo '<p class="error-message" id="new_password_error">' . esc_html($errors['new_password']) . '</p>'; ?>
</div>
</div>
<div class="form-row">
<label for="confirm_new_password">Confirm New Password</label>
<input type="password" name="confirm_new_password" id="confirm_new_password" aria-describedby="confirm_new_password_error">
<?php if (isset($errors['confirm_new_password'])) echo '<p class="error-message" id="confirm_new_password_error">' . esc_html($errors['confirm_new_password']) . '</p>'; ?>
</div>
</div>
<div class="form-submit">
<button type="submit" class="ast-button ast-button-primary" name="hvac_update_profile">Update Profile</button>
</div>
</form>
</div>
</main>
</div>
<style>
/* Edit Profile specific styles */
.hvac-edit-profile-container {
max-width: 1200px;
margin: 0 auto;
}
.hvac-message {
margin-bottom: 20px;
padding: 15px;
border-radius: 5px;
}
.hvac-message-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.hvac-message-error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.form-section {
margin-bottom: 40px;
background: #f8f9fa;
border-radius: 8px;
padding: 20px;
border: 1px solid #e9ecef;
}
.form-section h3 {
margin-top: 0;
margin-bottom: 20px;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}
.form-row {
margin-bottom: 20px;
}
.form-row label {
display: block;
margin-bottom: 5px;
}
.form-row input[type="text"],
.form-row input[type="email"],
.form-row input[type="tel"],
.form-row input[type="url"],
.form-row input[type="password"],
.form-row input[type="number"],
.form-row select,
.form-row textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.form-row textarea {
min-height: 100px;
}
.form-row small {
display: block;
margin-top: 5px;
color: #666;
}
.form-row-half {
display: flex;
gap: 20px;
}
.form-row-half > div {
flex: 1;
}
.radio-group,
.checkbox-group {
display: flex;
flex-wrap: wrap;
gap: 15px;
margin-top: 10px;
}
.radio-group label,
.checkbox-group label {
display: flex;
align-items: center;
margin-bottom: 0;
}
.radio-group input,
.checkbox-group input {
margin-right: 5px;
}
.profile-image-preview {
margin-bottom: 15px;
}
.profile-image-preview img {
border-radius: 50%;
object-fit: cover;
}
.hvac-profile-image-placeholder {
width: 150px;
height: 150px;
border-radius: 50%;
background-color: #0B5C7D;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
font-weight: bold;
}
.form-submit {
margin-top: 30px;
}
.form-submit button {
padding: 12px 20px;
font-size: 16px;
cursor: pointer;
}
.optional-section {
font-size: 0.8em;
font-weight: normal;
color: #666;
}
.section-description {
margin-top: -10px;
margin-bottom: 20px;
color: #666;
}
.error-message {
color: #dc3545;
margin-top: 5px;
font-size: 0.9em;
}
@media (max-width: 768px) {
.form-row-half {
flex-direction: column;
gap: 10px;
}
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const countrySelect = document.getElementById('user_country');
const stateSelect = document.getElementById('user_state');
const stateOtherInput = document.getElementById('user_state_other');
// US States data
const usStates = <?php echo json_encode(get_us_states()); ?>;
// Canadian Provinces data
const caProvinces = <?php echo json_encode(get_canadian_provinces()); ?>;
// Function to populate state/province dropdown
function populateStateProvinceDropdown(country) {
// Clear current options
stateSelect.innerHTML = '';
// Add default option
const defaultOption = document.createElement('option');
defaultOption.value = '';
defaultOption.textContent = 'Select State/Province';
stateSelect.appendChild(defaultOption);
// Add 'Other' option
const otherOption = document.createElement('option');
otherOption.value = 'Other';
otherOption.textContent = 'Other';
let options = [];
if (country === 'United States') {
// Add US states
options = Object.entries(usStates).map(([code, name]) => ({code, name}));
} else if (country === 'Canada') {
// Add Canadian provinces
options = Object.entries(caProvinces).map(([code, name]) => ({code, name}));
} else {
// For other countries, select 'Other' by default
otherOption.selected = true;
stateOtherInput.style.display = '';
}
// Add options to select
options.forEach(option => {
const optionElement = document.createElement('option');
optionElement.value = option.name;
optionElement.textContent = option.name;
// Check if this option should be selected
const currentState = "<?php echo esc_js($user_meta['user_state']); ?>";
if (option.name === currentState) {
optionElement.selected = true;
}
stateSelect.appendChild(optionElement);
});
// Add 'Other' option at the end
stateSelect.appendChild(otherOption);
// Show/hide 'Other' input based on selection
toggleStateOtherInput();
}
// Function to toggle display of the 'Other' state input
function toggleStateOtherInput() {
if (stateSelect.value === 'Other') {
stateOtherInput.style.display = '';
stateOtherInput.required = true;
} else {
stateOtherInput.style.display = 'none';
stateOtherInput.required = false;
}
}
// Initialize state dropdown based on current country
populateStateProvinceDropdown(countrySelect.value);
// Handle country change event
countrySelect.addEventListener('change', function() {
populateStateProvinceDropdown(this.value);
});
// Handle state change event
stateSelect.addEventListener('change', function() {
toggleStateOtherInput();
});
// Password validation
const newPasswordInput = document.getElementById('new_password');
const confirmPasswordInput = document.getElementById('confirm_new_password');
confirmPasswordInput.addEventListener('input', function() {
if (newPasswordInput.value !== this.value) {
this.setCustomValidity('Passwords do not match');
} else {
this.setCustomValidity('');
}
});
newPasswordInput.addEventListener('input', function() {
if (confirmPasswordInput.value && confirmPasswordInput.value !== this.value) {
confirmPasswordInput.setCustomValidity('Passwords do not match');
} else {
confirmPasswordInput.setCustomValidity('');
}
});
});
</script>
<?php
get_footer(); // Use theme's footer