feat(master-trainer): Enhance profile edit page with all fields and password reset
Some checks failed
HVAC Plugin CI/CD Pipeline / Security Analysis (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Code Quality & Standards (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Unit Tests (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Integration Tests (push) Has been cancelled
Security Monitoring & Compliance / Dependency Vulnerability Scan (push) Has been cancelled
Security Monitoring & Compliance / Secrets & Credential Scan (push) Has been cancelled
Security Monitoring & Compliance / WordPress Security Analysis (push) Has been cancelled
Security Monitoring & Compliance / Static Code Security Analysis (push) Has been cancelled
Security Monitoring & Compliance / Security Compliance Validation (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Deploy to Production (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Notification (push) Has been cancelled
Security Monitoring & Compliance / Security Summary Report (push) Has been cancelled
Security Monitoring & Compliance / Security Team Notification (push) Has been cancelled
Some checks failed
HVAC Plugin CI/CD Pipeline / Security Analysis (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Code Quality & Standards (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Unit Tests (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Integration Tests (push) Has been cancelled
Security Monitoring & Compliance / Dependency Vulnerability Scan (push) Has been cancelled
Security Monitoring & Compliance / Secrets & Credential Scan (push) Has been cancelled
Security Monitoring & Compliance / WordPress Security Analysis (push) Has been cancelled
Security Monitoring & Compliance / Static Code Security Analysis (push) Has been cancelled
Security Monitoring & Compliance / Security Compliance Validation (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Deploy to Production (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Notification (push) Has been cancelled
Security Monitoring & Compliance / Security Summary Report (push) Has been cancelled
Security Monitoring & Compliance / Security Team Notification (push) Has been cancelled
- Fix button styling with scoped CSS to avoid theme conflicts - Add all trainer profile fields (6 sections: Profile Settings, Certification, Personal Info, Professional Info, Business Info, Location) - Add "Send Password Reset Email" button for master trainers - Add AJAX handler for secure password reset functionality - Update Status.md with session details Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
503932e0c7
commit
23dcd158ec
3 changed files with 871 additions and 38 deletions
56
Status.md
56
Status.md
|
|
@ -1,12 +1,60 @@
|
|||
# HVAC Community Events - Project Status
|
||||
|
||||
**Last Updated:** January 5, 2026
|
||||
**Current Session:** TEC Community Events Dependency Analysis - Complete
|
||||
**Version:** 2.1.11 (Deployed to Production)
|
||||
**Last Updated:** January 9, 2026
|
||||
**Current Session:** Master Trainer Profile Edit Enhancement - Complete
|
||||
**Version:** 2.1.12 (Deployed to Production)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 CURRENT SESSION - TEC COMMUNITY EVENTS DEPENDENCY ANALYSIS (Jan 5, 2026)
|
||||
## 🎯 CURRENT SESSION - MASTER TRAINER PROFILE EDIT ENHANCEMENT (Jan 9, 2026)
|
||||
|
||||
### Status: ✅ **COMPLETE - Deployed to Production**
|
||||
|
||||
**Objective:** Fix broken button styling and add all trainer profile fields to the master trainer profile edit page.
|
||||
|
||||
**Issues Fixed:**
|
||||
1. **Button Styling Broken** - "Back to Dashboard" and "Cancel" buttons appeared faded/invisible due to CSS conflicts with theme
|
||||
2. **Missing Profile Fields** - Only showing basic name fields, needed all trainer profile fields
|
||||
3. **No Password Reset** - Master trainers couldn't trigger password resets for trainers they manage
|
||||
|
||||
### Changes Made
|
||||
|
||||
1. ✅ **Fixed Button Styling** (`templates/page-master-trainer-profile-edit-simple.php`)
|
||||
- Added scoped CSS with new class names (`hvac-btn-primary`, `hvac-btn-secondary`, `hvac-btn-outline`)
|
||||
- Avoids theme CSS conflicts by using page-specific selectors
|
||||
- Proper colors, hover states, and responsive behavior
|
||||
|
||||
2. ✅ **Added All Profile Fields** (6 complete sections)
|
||||
- **Profile Settings:** Visibility (Public/Private)
|
||||
- **Certification Information:** Status, Type, Date Certified
|
||||
- **Personal Information:** Name, Email, LinkedIn URL, Bio
|
||||
- **Professional Information:** Accreditation, Training Audience/Formats/Locations/Resources (checkboxes)
|
||||
- **Business Information:** Business Type, Revenue Target, Application Details
|
||||
- **Location Information:** City, State, Country, Coordinates with re-geocode button
|
||||
|
||||
3. ✅ **Added Password Reset Button**
|
||||
- New "Send Password Reset Email" button in Personal Information section
|
||||
- Uses WordPress built-in `retrieve_password()` function
|
||||
- Shows status feedback (Sending... / Sent! / Error)
|
||||
- Requires master trainer or admin permissions
|
||||
- All actions logged for audit trail
|
||||
|
||||
4. ✅ **Added AJAX Handler** (`includes/class-hvac-ajax-handlers.php`)
|
||||
- New `hvac_send_password_reset` endpoint
|
||||
- Nonce verification, permission checks, input validation
|
||||
- Secure implementation using WordPress core functions
|
||||
|
||||
### Files Modified
|
||||
- `templates/page-master-trainer-profile-edit-simple.php` - Complete rewrite with all fields
|
||||
- `includes/class-hvac-ajax-handlers.php` - Added password reset handler
|
||||
|
||||
### URLs
|
||||
- **Production:** `https://upskillhvac.com/master-trainer/edit-trainer-profile/?user_id=75`
|
||||
- **Staging:** `https://upskill-staging.measurequick.com/master-trainer/edit-trainer-profile/?user_id=75`
|
||||
|
||||
---
|
||||
|
||||
## 📋 PREVIOUS SESSION - TEC COMMUNITY EVENTS DEPENDENCY ANALYSIS (Jan 5, 2026)
|
||||
|
||||
### Status: ✅ **COMPLETE - Documented as Technical Debt**
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,10 @@ class HVAC_Ajax_Handlers {
|
|||
// Enhanced approval endpoint (wrapper for existing)
|
||||
add_action('wp_ajax_hvac_approve_trainer_v2', array($this, 'approve_trainer_secure'));
|
||||
add_action('wp_ajax_nopriv_hvac_approve_trainer_v2', array($this, 'unauthorized_access'));
|
||||
|
||||
// Password reset endpoint for master trainers
|
||||
add_action('wp_ajax_hvac_send_password_reset', array($this, 'send_password_reset'));
|
||||
add_action('wp_ajax_nopriv_hvac_send_password_reset', array($this, 'unauthorized_access'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -960,6 +964,66 @@ class HVAC_Ajax_Handlers {
|
|||
$this->clear_trainer_stats_cache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send password reset email to a trainer
|
||||
*
|
||||
* Allows master trainers to trigger a password reset email for any trainer.
|
||||
* Uses WordPress built-in password reset functionality.
|
||||
*/
|
||||
public function send_password_reset() {
|
||||
// Verify nonce
|
||||
if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_profile_edit')) {
|
||||
wp_send_json_error('Invalid security token', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if user is logged in
|
||||
if (!is_user_logged_in()) {
|
||||
wp_send_json_error('You must be logged in', 401);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if user has permission (master trainer or admin)
|
||||
$current_user = wp_get_current_user();
|
||||
if (!in_array('hvac_master_trainer', $current_user->roles) && !in_array('administrator', $current_user->roles)) {
|
||||
wp_send_json_error('You do not have permission to perform this action', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get target user ID
|
||||
$user_id = isset($_POST['user_id']) ? absint($_POST['user_id']) : 0;
|
||||
if (!$user_id) {
|
||||
wp_send_json_error('Invalid user ID', 400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get target user
|
||||
$user = get_userdata($user_id);
|
||||
if (!$user) {
|
||||
wp_send_json_error('User not found', 404);
|
||||
return;
|
||||
}
|
||||
|
||||
// Use WordPress built-in password reset
|
||||
$result = retrieve_password($user->user_login);
|
||||
|
||||
if (is_wp_error($result)) {
|
||||
wp_send_json_error($result->get_error_message(), 500);
|
||||
return;
|
||||
}
|
||||
|
||||
// Log the action
|
||||
error_log(sprintf(
|
||||
'[HVAC] Password reset email sent for user %d (%s) by master trainer %d (%s)',
|
||||
$user_id,
|
||||
$user->user_email,
|
||||
$current_user->ID,
|
||||
$current_user->user_login
|
||||
));
|
||||
|
||||
wp_send_json_success('Password reset email sent to ' . $user->user_email);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the handlers
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
/**
|
||||
* Template Name: Master Trainer Profile Edit (Simple)
|
||||
* Description: Simplified template for master trainers to edit any trainer profile
|
||||
* Description: Template for master trainers to edit any trainer profile
|
||||
*/
|
||||
|
||||
// Define constant to indicate we are in a page template
|
||||
|
|
@ -57,8 +57,300 @@ if (!$profile) {
|
|||
}
|
||||
|
||||
$profile_meta = $profile_manager->get_profile_meta($profile->ID);
|
||||
|
||||
// Get coordinates if available
|
||||
$coordinates = null;
|
||||
$geocoding_status = ['status' => 'unknown'];
|
||||
if (class_exists('HVAC_Geocoding_Service')) {
|
||||
try {
|
||||
$geocoding_service = HVAC_Geocoding_Service::get_instance();
|
||||
$coordinates = $geocoding_service->get_coordinates($profile->ID);
|
||||
$geocoding_status = $geocoding_service->get_geocoding_status($profile->ID);
|
||||
} catch (Exception $e) {
|
||||
error_log('Geocoding service error in master trainer profile edit: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<style>
|
||||
/* Scoped styles for this page to ensure buttons render correctly */
|
||||
.hvac-master-trainer-profile-edit-page .hvac-btn-primary {
|
||||
display: inline-block;
|
||||
padding: 12px 24px;
|
||||
background-color: #0073aa;
|
||||
color: #ffffff !important;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-btn-primary:hover {
|
||||
background-color: #005a87;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-btn-secondary {
|
||||
display: inline-block;
|
||||
padding: 12px 24px;
|
||||
background-color: #54595f;
|
||||
color: #ffffff !important;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-btn-secondary:hover {
|
||||
background-color: #3a3f44;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-btn-outline {
|
||||
display: inline-block;
|
||||
padding: 10px 22px;
|
||||
background-color: transparent;
|
||||
color: #0073aa !important;
|
||||
border: 2px solid #0073aa;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-btn-outline:hover {
|
||||
background-color: #e6f3fb;
|
||||
color: #0073aa !important;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-header-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-top: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-top: 2rem;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-section {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
margin-bottom: 24px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-section h3 {
|
||||
margin: 0 0 20px 0;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 2px solid #0073aa;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-section h3 small {
|
||||
font-weight: normal;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row input[type="text"],
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row input[type="email"],
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row input[type="url"],
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row input[type="number"],
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row input[type="date"],
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row select,
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row textarea {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 15px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row input:focus,
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row select:focus,
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row textarea:focus {
|
||||
border-color: #0073aa;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px rgba(0, 115, 170, 0.2);
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row-half {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row-half > div {
|
||||
flex: 1;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-checkbox-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-checkbox-group label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
padding: 8px 12px;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-checkbox-group label:hover {
|
||||
background: #e6f3fb;
|
||||
border-color: #0073aa;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-checkbox-group input[type="checkbox"]:checked + span,
|
||||
.hvac-master-trainer-profile-edit-page .hvac-checkbox-group label:has(input:checked) {
|
||||
background: #e6f3fb;
|
||||
border-color: #0073aa;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-field-description {
|
||||
margin-top: 4px;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-profile-status-overview {
|
||||
background: #f8f9fa;
|
||||
padding: 16px 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-status-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 24px;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-status-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-status-label {
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-status-value {
|
||||
padding: 4px 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-status-value.status-public {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-status-value.status-private {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-status-value.status-success {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-status-value.status-pending {
|
||||
background: #fff3cd;
|
||||
color: #856404;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-status-value.status-error,
|
||||
.hvac-master-trainer-profile-edit-page .hvac-status-value.status-unknown {
|
||||
background: #f0f0f1;
|
||||
color: #666;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-coordinates-display {
|
||||
background: #f8f9fa;
|
||||
padding: 12px 16px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-btn-small {
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .notice {
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .notice-success {
|
||||
background: #d4edda;
|
||||
border-color: #28a745;
|
||||
color: #155724;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .notice-error {
|
||||
background: #f8d7da;
|
||||
border-color: #dc3545;
|
||||
color: #721c24;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .notice-info {
|
||||
background: #d1ecf1;
|
||||
border-color: #17a2b8;
|
||||
color: #0c5460;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-password-reset-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-reset-status {
|
||||
font-size: 14px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-reset-status.success {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-reset-status.error {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-reset-status.sending {
|
||||
background: #fff3cd;
|
||||
color: #856404;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-row-half {
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-header-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-actions .hvac-btn-primary,
|
||||
.hvac-master-trainer-profile-edit-page .hvac-form-actions .hvac-btn-secondary {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
.hvac-master-trainer-profile-edit-page .hvac-status-grid {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="hvac-page-wrapper hvac-master-trainer-profile-edit-page">
|
||||
<?php
|
||||
// Display master trainer navigation menu
|
||||
|
|
@ -67,49 +359,385 @@ $profile_meta = $profile_manager->get_profile_meta($profile->ID);
|
|||
}
|
||||
?>
|
||||
|
||||
<?php
|
||||
// Display breadcrumbs
|
||||
if (class_exists('HVAC_Breadcrumbs')) {
|
||||
echo HVAC_Breadcrumbs::instance()->render_breadcrumbs();
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<div class="hvac-master-trainer-profile-edit">
|
||||
<div class="hvac-page-header">
|
||||
<h1>Edit Trainer Profile: <?php echo esc_html($edit_user->display_name); ?></h1>
|
||||
<div class="hvac-header-actions">
|
||||
<a href="/master-trainer/master-dashboard/" class="hvac-button hvac-button-secondary">Back to Dashboard</a>
|
||||
<a href="/master-trainer/master-dashboard/" class="hvac-btn-secondary">Back to Dashboard</a>
|
||||
<?php if (get_option('hvac_default_profile_visibility') === 'public' || get_post_meta($profile->ID, 'is_public_profile', true) === '1'): ?>
|
||||
<a href="<?php echo get_permalink($profile->ID); ?>" class="hvac-btn-outline" target="_blank">View Public Profile</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Success/Error Messages -->
|
||||
<div id="hvac-profile-messages"></div>
|
||||
|
||||
<!-- Profile Status Overview -->
|
||||
<div class="hvac-profile-status-overview">
|
||||
<div class="hvac-status-grid">
|
||||
<div class="hvac-status-item">
|
||||
<span class="hvac-status-label">Profile Status:</span>
|
||||
<span class="hvac-status-value <?php echo get_post_meta($profile->ID, 'is_public_profile', true) === '1' ? 'status-public' : 'status-private'; ?>">
|
||||
<?php echo get_post_meta($profile->ID, 'is_public_profile', true) === '1' ? 'Public' : 'Private'; ?>
|
||||
</span>
|
||||
</div>
|
||||
<div class="hvac-status-item">
|
||||
<span class="hvac-status-label">Geocoding:</span>
|
||||
<span class="hvac-status-value status-<?php echo esc_attr($geocoding_status['status'] ?? 'unknown'); ?>">
|
||||
<?php echo esc_html(ucfirst($geocoding_status['status'] ?? 'Unknown')); ?>
|
||||
</span>
|
||||
</div>
|
||||
<div class="hvac-status-item">
|
||||
<span class="hvac-status-label">Last Updated:</span>
|
||||
<span class="hvac-status-value"><?php echo human_time_diff(strtotime($profile->post_modified), current_time('timestamp')) . ' ago'; ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id="hvac-master-profile-form" class="hvac-form" enctype="multipart/form-data">
|
||||
<?php wp_nonce_field('hvac_profile_edit', 'hvac_profile_nonce'); ?>
|
||||
<input type="hidden" name="edit_user_id" value="<?php echo $edit_user_id; ?>" />
|
||||
<input type="hidden" name="profile_id" value="<?php echo $profile->ID; ?>" />
|
||||
<input type="hidden" name="edit_user_id" value="<?php echo esc_attr($edit_user_id); ?>" />
|
||||
<input type="hidden" name="profile_id" value="<?php echo esc_attr($profile->ID); ?>" />
|
||||
|
||||
<!-- Basic Information Test -->
|
||||
<!-- Profile Settings -->
|
||||
<div class="hvac-form-section">
|
||||
<h3>Basic Information</h3>
|
||||
<h3>Profile Settings</h3>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="is_public_profile">Profile Visibility</label>
|
||||
<select id="is_public_profile" name="is_public_profile">
|
||||
<option value="0" <?php selected(get_post_meta($profile->ID, 'is_public_profile', true), '0'); ?>>Private</option>
|
||||
<option value="1" <?php selected(get_post_meta($profile->ID, 'is_public_profile', true), '1'); ?>>Public</option>
|
||||
</select>
|
||||
<p class="hvac-field-description">Public profiles are visible in the trainer directory</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Certification Information -->
|
||||
<div class="hvac-form-section">
|
||||
<h3>Certification Information <small>(Master Trainer Only)</small></h3>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="certification_status">Certification Status</label>
|
||||
<select id="certification_status" name="certification_status">
|
||||
<option value="">Select Status</option>
|
||||
<?php
|
||||
$status_options = [
|
||||
'Active' => 'Active',
|
||||
'Expired' => 'Expired',
|
||||
'Pending' => 'Pending',
|
||||
'Disabled' => 'Disabled'
|
||||
];
|
||||
$current_status = $profile_meta['certification_status'] ?? '';
|
||||
foreach ($status_options as $value => $label) {
|
||||
printf(
|
||||
'<option value="%s" %s>%s</option>',
|
||||
esc_attr($value),
|
||||
selected($current_status, $value, false),
|
||||
esc_html($label)
|
||||
);
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="certification_type">Certification Type</label>
|
||||
<select id="certification_type" name="certification_type">
|
||||
<option value="">Select Type</option>
|
||||
<?php
|
||||
$type_options = [
|
||||
'Certified measureQuick Trainer' => 'Certified measureQuick Trainer',
|
||||
'Certified measureQuick Champion' => 'Certified measureQuick Champion'
|
||||
];
|
||||
$current_type = $profile_meta['certification_type'] ?? '';
|
||||
foreach ($type_options as $value => $label) {
|
||||
printf(
|
||||
'<option value="%s" %s>%s</option>',
|
||||
esc_attr($value),
|
||||
selected($current_type, $value, false),
|
||||
esc_html($label)
|
||||
);
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="date_certified">Date Certified</label>
|
||||
<input type="date" id="date_certified" name="date_certified"
|
||||
value="<?php echo esc_attr($profile_meta['date_certified'] ?? ''); ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Personal Information -->
|
||||
<div class="hvac-form-section">
|
||||
<h3>Personal Information</h3>
|
||||
|
||||
<div class="hvac-form-row hvac-form-row-half">
|
||||
<div>
|
||||
<label for="trainer_first_name">First Name *</label>
|
||||
<input type="text" id="trainer_first_name" name="trainer_first_name" required
|
||||
value="<?php echo esc_attr($profile_meta['trainer_first_name'] ?? $edit_user->first_name); ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<div>
|
||||
<label for="trainer_last_name">Last Name *</label>
|
||||
<input type="text" id="trainer_last_name" name="trainer_last_name" required
|
||||
value="<?php echo esc_attr($profile_meta['trainer_last_name'] ?? $edit_user->last_name); ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="trainer_display_name">Display Name *</label>
|
||||
<input type="text" id="trainer_display_name" name="trainer_display_name" required
|
||||
value="<?php echo esc_attr($profile_meta['trainer_display_name'] ?? $edit_user->display_name); ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="trainer_email">Email Address</label>
|
||||
<input type="email" id="trainer_email" name="trainer_email"
|
||||
value="<?php echo esc_attr($edit_user->user_email); ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label>Password Management</label>
|
||||
<div class="hvac-password-reset-row">
|
||||
<button type="button" id="send-password-reset" class="hvac-btn-outline">
|
||||
Send Password Reset Email
|
||||
</button>
|
||||
<span id="password-reset-status" class="hvac-reset-status"></span>
|
||||
</div>
|
||||
<p class="hvac-field-description">Send a password reset link to the trainer's email address (<?php echo esc_html($edit_user->user_email); ?>)</p>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="linkedin_profile_url">LinkedIn Profile URL</label>
|
||||
<input type="url" id="linkedin_profile_url" name="linkedin_profile_url"
|
||||
value="<?php echo esc_attr($profile_meta['linkedin_profile_url'] ?? ''); ?>"
|
||||
placeholder="https://linkedin.com/in/username" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="biographical_info">Biographical Information</label>
|
||||
<textarea id="biographical_info" name="biographical_info" rows="6"><?php echo esc_textarea($profile->post_content); ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Professional Information -->
|
||||
<div class="hvac-form-section">
|
||||
<h3>Professional Information</h3>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="personal_accreditation">Personal Accreditation</label>
|
||||
<textarea id="personal_accreditation" name="personal_accreditation" rows="4"><?php echo esc_textarea($profile_meta['personal_accreditation'] ?? ''); ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label>Training Audience</label>
|
||||
<div class="hvac-checkbox-group">
|
||||
<?php
|
||||
$audience_terms = get_terms(['taxonomy' => 'training_audience', 'hide_empty' => false]);
|
||||
$current_audience_terms = get_the_terms($profile->ID, 'training_audience');
|
||||
$current_audience_names = $current_audience_terms && !is_wp_error($current_audience_terms)
|
||||
? wp_list_pluck($current_audience_terms, 'name') : [];
|
||||
|
||||
if (!is_wp_error($audience_terms) && !empty($audience_terms)) {
|
||||
foreach ($audience_terms as $term) {
|
||||
printf(
|
||||
'<label><input type="checkbox" name="training_audience[]" value="%s" %s> %s</label>',
|
||||
esc_attr($term->name),
|
||||
checked(in_array($term->name, $current_audience_names), true, false),
|
||||
esc_html($term->name)
|
||||
);
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label>Training Formats</label>
|
||||
<div class="hvac-checkbox-group">
|
||||
<?php
|
||||
$format_terms = get_terms(['taxonomy' => 'training_formats', 'hide_empty' => false]);
|
||||
$current_format_terms = get_the_terms($profile->ID, 'training_formats');
|
||||
$current_format_names = $current_format_terms && !is_wp_error($current_format_terms)
|
||||
? wp_list_pluck($current_format_terms, 'name') : [];
|
||||
|
||||
if (!is_wp_error($format_terms) && !empty($format_terms)) {
|
||||
foreach ($format_terms as $term) {
|
||||
printf(
|
||||
'<label><input type="checkbox" name="training_formats[]" value="%s" %s> %s</label>',
|
||||
esc_attr($term->name),
|
||||
checked(in_array($term->name, $current_format_names), true, false),
|
||||
esc_html($term->name)
|
||||
);
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label>Training Locations</label>
|
||||
<div class="hvac-checkbox-group">
|
||||
<?php
|
||||
$location_terms = get_terms(['taxonomy' => 'training_locations', 'hide_empty' => false]);
|
||||
$current_location_terms = get_the_terms($profile->ID, 'training_locations');
|
||||
$current_location_names = $current_location_terms && !is_wp_error($current_location_terms)
|
||||
? wp_list_pluck($current_location_terms, 'name') : [];
|
||||
|
||||
if (!is_wp_error($location_terms) && !empty($location_terms)) {
|
||||
foreach ($location_terms as $term) {
|
||||
printf(
|
||||
'<label><input type="checkbox" name="training_locations[]" value="%s" %s> %s</label>',
|
||||
esc_attr($term->name),
|
||||
checked(in_array($term->name, $current_location_names), true, false),
|
||||
esc_html($term->name)
|
||||
);
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label>Training Resources</label>
|
||||
<div class="hvac-checkbox-group">
|
||||
<?php
|
||||
$resource_terms = get_terms(['taxonomy' => 'training_resources', 'hide_empty' => false]);
|
||||
$current_resource_terms = get_the_terms($profile->ID, 'training_resources');
|
||||
$current_resource_names = $current_resource_terms && !is_wp_error($current_resource_terms)
|
||||
? wp_list_pluck($current_resource_terms, 'name') : [];
|
||||
|
||||
if (!is_wp_error($resource_terms) && !empty($resource_terms)) {
|
||||
foreach ($resource_terms as $term) {
|
||||
printf(
|
||||
'<label><input type="checkbox" name="training_resources[]" value="%s" %s> %s</label>',
|
||||
esc_attr($term->name),
|
||||
checked(in_array($term->name, $current_resource_names), true, false),
|
||||
esc_html($term->name)
|
||||
);
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Business Information -->
|
||||
<div class="hvac-form-section">
|
||||
<h3>Business Information</h3>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="business_type">Business Type</label>
|
||||
<select id="business_type" name="business_type">
|
||||
<option value="">Select Business Type</option>
|
||||
<?php
|
||||
$business_terms = get_terms(['taxonomy' => 'business_type', 'hide_empty' => false]);
|
||||
$current_terms = get_the_terms($profile->ID, 'business_type');
|
||||
$current_business_type = $current_terms && !is_wp_error($current_terms) ? $current_terms[0]->name : '';
|
||||
|
||||
if (!is_wp_error($business_terms) && !empty($business_terms)) {
|
||||
foreach ($business_terms as $term) {
|
||||
printf(
|
||||
'<option value="%s" %s>%s</option>',
|
||||
esc_attr($term->name),
|
||||
selected($current_business_type, $term->name, false),
|
||||
esc_html($term->name)
|
||||
);
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="annual_revenue_target">Annual Revenue Target</label>
|
||||
<input type="number" id="annual_revenue_target" name="annual_revenue_target"
|
||||
value="<?php echo esc_attr($profile_meta['annual_revenue_target'] ?? ''); ?>"
|
||||
placeholder="Enter amount in USD" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="application_details">Application Details</label>
|
||||
<textarea id="application_details" name="application_details" rows="4"><?php echo esc_textarea($profile_meta['application_details'] ?? ''); ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Location Information -->
|
||||
<div class="hvac-form-section">
|
||||
<h3>Location Information</h3>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="trainer_city">City</label>
|
||||
<input type="text" id="trainer_city" name="trainer_city"
|
||||
value="<?php echo esc_attr($profile_meta['trainer_city'] ?? ''); ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row hvac-form-row-half">
|
||||
<div>
|
||||
<label for="trainer_state">State/Province</label>
|
||||
<input type="text" id="trainer_state" name="trainer_state"
|
||||
value="<?php echo esc_attr($profile_meta['trainer_state'] ?? ''); ?>" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="trainer_country">Country</label>
|
||||
<select id="trainer_country" name="trainer_country">
|
||||
<option value="">Select Country</option>
|
||||
<?php
|
||||
$countries = [
|
||||
'United States' => 'United States',
|
||||
'Canada' => 'Canada',
|
||||
'United Kingdom' => 'United Kingdom',
|
||||
'Australia' => 'Australia',
|
||||
'New Zealand' => 'New Zealand',
|
||||
'Germany' => 'Germany',
|
||||
'France' => 'France',
|
||||
'Mexico' => 'Mexico',
|
||||
'Other' => 'Other'
|
||||
];
|
||||
$current_country = $profile_meta['trainer_country'] ?? '';
|
||||
|
||||
foreach ($countries as $code => $name) {
|
||||
printf(
|
||||
'<option value="%s" %s>%s</option>',
|
||||
esc_attr($code),
|
||||
selected($current_country, $code, false),
|
||||
esc_html($name)
|
||||
);
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($coordinates): ?>
|
||||
<div class="hvac-form-row">
|
||||
<label>Coordinates (Auto-generated)</label>
|
||||
<div class="hvac-coordinates-display">
|
||||
<strong>Latitude:</strong> <?php echo esc_html($coordinates['latitude']); ?><br>
|
||||
<strong>Longitude:</strong> <?php echo esc_html($coordinates['longitude']); ?><br>
|
||||
<strong>Formatted Address:</strong> <?php echo esc_html($coordinates['formatted_address'] ?? 'N/A'); ?><br>
|
||||
<strong>Last Updated:</strong> <?php echo $coordinates['last_geocoded'] ? human_time_diff($coordinates['last_geocoded'], current_time('timestamp')) . ' ago' : 'Never'; ?>
|
||||
</div>
|
||||
<button type="button" id="re-geocode" class="hvac-btn-secondary hvac-btn-small">Re-geocode Address</button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-actions">
|
||||
<button type="submit" class="hvac-button hvac-button-primary">Save Profile Changes</button>
|
||||
<a href="/master-trainer/master-dashboard/" class="hvac-button hvac-button-secondary">Cancel</a>
|
||||
<button type="submit" class="hvac-btn-primary">Save Profile Changes</button>
|
||||
<a href="/master-trainer/master-dashboard/" class="hvac-btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -117,10 +745,11 @@ $profile_meta = $profile_manager->get_profile_meta($profile->ID);
|
|||
</div>
|
||||
|
||||
<script>
|
||||
// Basic form functionality
|
||||
// Initialize form state management
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const form = document.getElementById('hvac-master-profile-form');
|
||||
const saveButton = form.querySelector('button[type="submit"]');
|
||||
const originalButtonText = saveButton.textContent;
|
||||
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
|
@ -128,15 +757,107 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
saveButton.textContent = 'Saving...';
|
||||
saveButton.disabled = true;
|
||||
|
||||
// For now, just show a test message
|
||||
document.getElementById('hvac-profile-messages').innerHTML =
|
||||
'<div class="notice notice-info"><p>Profile edit form is working! (Test mode)</p></div>';
|
||||
const formData = new FormData(form);
|
||||
formData.append('action', 'hvac_save_trainer_profile');
|
||||
|
||||
setTimeout(() => {
|
||||
saveButton.textContent = 'Save Profile Changes';
|
||||
fetch(hvac_ajax.ajax_url, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const messagesDiv = document.getElementById('hvac-profile-messages');
|
||||
|
||||
if (data.success) {
|
||||
messagesDiv.innerHTML = '<div class="notice notice-success"><p>Profile updated successfully!</p></div>';
|
||||
} else {
|
||||
messagesDiv.innerHTML = '<div class="notice notice-error"><p>Error: ' + (data.data || 'Unknown error occurred') + '</p></div>';
|
||||
}
|
||||
|
||||
// Scroll to messages
|
||||
messagesDiv.scrollIntoView({ behavior: 'smooth' });
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
document.getElementById('hvac-profile-messages').innerHTML = '<div class="notice notice-error"><p>Network error occurred. Please try again.</p></div>';
|
||||
})
|
||||
.finally(() => {
|
||||
saveButton.textContent = originalButtonText;
|
||||
saveButton.disabled = false;
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
// Re-geocode button functionality
|
||||
const regeocodeBUtton = document.getElementById('re-geocode');
|
||||
if (regeocodeBUtton) {
|
||||
regeocodeBUtton.addEventListener('click', function() {
|
||||
this.textContent = 'Geocoding...';
|
||||
this.disabled = true;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('action', 'hvac_regeocode_profile');
|
||||
formData.append('profile_id', document.querySelector('input[name="profile_id"]').value);
|
||||
formData.append('nonce', document.querySelector('input[name="hvac_profile_nonce"]').value);
|
||||
|
||||
fetch(hvac_ajax.ajax_url, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
location.reload(); // Reload to show updated coordinates
|
||||
} else {
|
||||
alert('Geocoding failed: ' + (data.data || 'Unknown error'));
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.textContent = 'Re-geocode Address';
|
||||
this.disabled = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Password reset button functionality
|
||||
const passwordResetButton = document.getElementById('send-password-reset');
|
||||
if (passwordResetButton) {
|
||||
passwordResetButton.addEventListener('click', function() {
|
||||
const statusSpan = document.getElementById('password-reset-status');
|
||||
|
||||
this.textContent = 'Sending...';
|
||||
this.disabled = true;
|
||||
statusSpan.className = 'hvac-reset-status sending';
|
||||
statusSpan.textContent = 'Sending reset email...';
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('action', 'hvac_send_password_reset');
|
||||
formData.append('user_id', document.querySelector('input[name="edit_user_id"]').value);
|
||||
formData.append('nonce', document.querySelector('input[name="hvac_profile_nonce"]').value);
|
||||
|
||||
fetch(hvac_ajax.ajax_url, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
statusSpan.className = 'hvac-reset-status success';
|
||||
statusSpan.textContent = 'Password reset email sent!';
|
||||
} else {
|
||||
statusSpan.className = 'hvac-reset-status error';
|
||||
statusSpan.textContent = 'Error: ' + (data.data || 'Failed to send email');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
statusSpan.className = 'hvac-reset-status error';
|
||||
statusSpan.textContent = 'Network error occurred';
|
||||
})
|
||||
.finally(() => {
|
||||
this.textContent = 'Send Password Reset Email';
|
||||
this.disabled = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue