feat: Major registration refactor and new trainer management pages
- Refactored registration form:
* Moved Application Details to Personal Information section
* Renamed Business Information to Training Organization Information
* Added required Organization Logo upload with media library integration
* Added Headquarters location fields (City, State/Province, Country)
* Moved training-related fields into Organization section
* Created conditional Training Venue Information section with auto-population
- Created comprehensive venue management system:
* Training Venues List page (/trainer/venue/list) with filtering and pagination
* Manage Venue page (/trainer/venue/manage) for create/edit operations
* Full integration with The Events Calendar venue post type
* AJAX-powered forms with real-time validation
- Created trainer profile system:
* Trainer Profile view page (/trainer/profile) with stats and certifications
* Profile Edit page (/trainer/profile/edit) with photo upload
* Years of experience tracking and professional information
* Integration with user meta and custom fields
- Created training organizers management:
* Organizers List page (/trainer/organizer/list) with search functionality
* Manage Organizer page (/trainer/organizer/manage) for CRUD operations
* Organization logo upload and headquarters tracking
* Full integration with The Events Calendar organizer post type
- Technical improvements:
* Modular PHP class architecture for each feature
* Comprehensive AJAX handlers with security nonces
* Responsive CSS design for all new pages
* JavaScript form validation and dynamic behavior
* Proper WordPress and TEC API integration
All new features follow hierarchical URL structure and include breadcrumb navigation.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
00b3b2008b
commit
e4f079a89c
22 changed files with 7074 additions and 123 deletions
|
|
@ -28,5 +28,6 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||
- **Plugin Architecture Refactoring (2025-07-28)**: Implemented modular architecture with single-responsibility classes. Created HVAC_Shortcodes for centralized shortcode management, HVAC_Scripts_Styles for asset management, and HVAC_Route_Manager for URL routing. Eliminated duplicate functionality between HVAC_Plugin and HVAC_Community_Events. All components now use singleton pattern to prevent duplicate initialization. Fixed jQuery selector errors and duplicate content issues. See docs/ARCHITECTURE.md for details.
|
||||
- **Master Dashboard URL Fix (2025-07-29)**: Fixed critical issue where master dashboard was showing trainer dashboard content. Root cause: Both trainer and master dashboards had the same page slug "dashboard", causing WordPress to load the wrong page. Solution: Changed master dashboard URL from `/master-trainer/dashboard/` to `/master-trainer/master-dashboard/`, updated all code references, removed conflicting legacy redirects. Master dashboard now correctly displays master trainer content with aggregate statistics and trainer performance analytics.
|
||||
- **Event Manage Page CSS and Header Fix (2025-07-30)**: Resolved persistent CSS override and duplicate header issues on the trainer/event/manage/ page. Root causes: CSS specificity conflicts with theme styles, header being added via both template and tribe hook. Solution: Scoped all CSS rules to `.hvac-event-manage-wrapper`, moved navigation header directly into page template, disabled duplicate tribe hook, added theme override styles. Page now displays correctly with single header, proper 1200px max-width layout, 20px padding, and consistent styling matching other dashboard pages.
|
||||
- **Major Plugin Update - Registration Refactor and New Trainer Pages (2025-07-30)**: Implemented comprehensive updates to HVAC plugin. Registration form refactored: moved Application Details to Personal Information, renamed Business Information to Training Organization Information with TEC integration, added required Organization Logo upload, added Headquarters location fields, created conditional Training Venue section. New trainer pages created: Training Venues system (/trainer/venue/list, /trainer/venue/manage) with full CRUD operations and TEC venue integration; Trainer Profile system (/trainer/profile, /trainer/profile/edit) with photo upload, certifications, stats tracking; Training Organizers system (/trainer/organizer/list, /trainer/organizer/manage) with logo upload and headquarters tracking. All systems use AJAX forms, real-time validation, responsive design, and proper WordPress/TEC integration.
|
||||
|
||||
[... rest of the existing content remains unchanged ...]
|
||||
434
assets/css/hvac-organizers.css
Normal file
434
assets/css/hvac-organizers.css
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
/**
|
||||
* HVAC Organizers Styles
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
* @version 2.0.0
|
||||
*/
|
||||
|
||||
/* Page Layout */
|
||||
.hvac-organizers-list,
|
||||
.hvac-organizer-manage {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
/* Page Header */
|
||||
.hvac-page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.hvac-page-header h1 {
|
||||
margin: 0;
|
||||
color: #0274be;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
/* Breadcrumb */
|
||||
.hvac-breadcrumb {
|
||||
margin-bottom: 1.5rem;
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.hvac-breadcrumb a {
|
||||
color: #0274be;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.hvac-breadcrumb a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Filters */
|
||||
.hvac-organizers-filters {
|
||||
background-color: #f5f5f5;
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.hvac-filter-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.hvac-filter-row {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hvac-filter-group {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.hvac-filter-group input {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* Table */
|
||||
.hvac-organizers-table-wrapper {
|
||||
overflow-x: auto;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.hvac-organizers-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background-color: white;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.hvac-organizers-table th {
|
||||
background-color: #f8f9fa;
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.hvac-organizers-table td {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.hvac-organizers-table tbody tr:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.hvac-no-results {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
padding: 3rem !important;
|
||||
}
|
||||
|
||||
/* Logo Styles */
|
||||
.hvac-org-logo {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.hvac-org-logo img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.hvac-logo-placeholder {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-color: #0274be;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.hvac-logo-placeholder-large {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background-color: #f0f0f0;
|
||||
color: #999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1rem;
|
||||
border-radius: 8px;
|
||||
border: 2px dashed #ddd;
|
||||
}
|
||||
|
||||
/* Logo Upload */
|
||||
.hvac-org-logo-upload {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.hvac-current-logo img {
|
||||
max-width: 200px;
|
||||
max-height: 200px;
|
||||
object-fit: contain;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.hvac-logo-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.hvac-help-text {
|
||||
color: #666;
|
||||
font-size: 0.875rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.hvac-button {
|
||||
display: inline-block;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.hvac-button-primary {
|
||||
background-color: #0274be;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hvac-button-primary:hover {
|
||||
background-color: #005fa3;
|
||||
}
|
||||
|
||||
.hvac-button-secondary {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hvac-button-secondary:hover {
|
||||
background-color: #5a6268;
|
||||
}
|
||||
|
||||
.hvac-button-danger {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hvac-button-danger:hover {
|
||||
background-color: #c82333;
|
||||
}
|
||||
|
||||
.hvac-button-danger-outline {
|
||||
background-color: transparent;
|
||||
color: #dc3545;
|
||||
border: 1px solid #dc3545;
|
||||
}
|
||||
|
||||
.hvac-button-danger-outline:hover {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hvac-button-small {
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.hvac-text-muted {
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.hvac-pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.hvac-pagination a,
|
||||
.hvac-pagination span {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.hvac-pagination a:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.hvac-pagination .current {
|
||||
background-color: #0274be;
|
||||
color: white;
|
||||
border-color: #0274be;
|
||||
}
|
||||
|
||||
/* Form Styles */
|
||||
.hvac-form {
|
||||
background-color: white;
|
||||
padding: 2rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.hvac-form-section {
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.hvac-form-section:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.hvac-form-section h3 {
|
||||
margin-bottom: 1.5rem;
|
||||
color: #333;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.hvac-form-row {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.hvac-form-row label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.hvac-form-row input,
|
||||
.hvac-form-row select,
|
||||
.hvac-form-row textarea {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.hvac-form-row input:focus,
|
||||
.hvac-form-row select:focus,
|
||||
.hvac-form-row textarea:focus {
|
||||
outline: none;
|
||||
border-color: #0274be;
|
||||
box-shadow: 0 0 0 3px rgba(2, 116, 190, 0.1);
|
||||
}
|
||||
|
||||
.hvac-form-row-half {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.hvac-form-row-half > div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.hvac-form-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-top: 2rem;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.hvac-form-actions .hvac-button-danger {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* Messages */
|
||||
.hvac-message {
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 1.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.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 Error Styles */
|
||||
.hvac-form-error {
|
||||
border-color: #dc3545 !important;
|
||||
}
|
||||
|
||||
.hvac-error-message {
|
||||
display: block;
|
||||
color: #dc3545;
|
||||
font-size: 0.875rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.hvac-organizers-list,
|
||||
.hvac-organizer-manage {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.hvac-page-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.hvac-filter-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.hvac-filter-group {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hvac-form-row-half {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.hvac-form-actions {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.hvac-form-actions .hvac-button-danger {
|
||||
margin-left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hvac-organizers-table {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.hvac-organizers-table th,
|
||||
.hvac-organizers-table td {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.hvac-org-logo-upload {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hvac-logo-actions {
|
||||
width: 100%;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
1003
assets/css/hvac-registration.css
Normal file
1003
assets/css/hvac-registration.css
Normal file
File diff suppressed because it is too large
Load diff
419
assets/css/hvac-trainer-profile.css
Normal file
419
assets/css/hvac-trainer-profile.css
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
/**
|
||||
* HVAC Trainer Profile Styles
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
* @version 2.0.0
|
||||
*/
|
||||
|
||||
/* Page Layout */
|
||||
.hvac-trainer-profile-view,
|
||||
.hvac-trainer-profile-edit {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
/* Page Header */
|
||||
.hvac-page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.hvac-page-header h1 {
|
||||
margin: 0;
|
||||
color: #0274be;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
/* Breadcrumb */
|
||||
.hvac-breadcrumb {
|
||||
margin-bottom: 1.5rem;
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.hvac-breadcrumb a {
|
||||
color: #0274be;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.hvac-breadcrumb a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Profile Content Layout */
|
||||
.hvac-profile-content {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
/* Profile Sidebar */
|
||||
.hvac-profile-sidebar {
|
||||
flex: 0 0 300px;
|
||||
}
|
||||
|
||||
/* Profile Photo */
|
||||
.hvac-profile-photo {
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hvac-profile-photo img {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 5px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.hvac-profile-photo-placeholder {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 50%;
|
||||
background-color: #0274be;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 3rem;
|
||||
font-weight: 600;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Profile Stats */
|
||||
.hvac-profile-stats {
|
||||
background-color: #f8f9fa;
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.hvac-stat-item {
|
||||
text-align: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.hvac-stat-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.hvac-stat-value {
|
||||
display: block;
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: #0274be;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.hvac-stat-label {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
color: #666;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
/* Profile Main Content */
|
||||
.hvac-profile-main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.hvac-profile-section {
|
||||
background-color: white;
|
||||
padding: 2rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.hvac-profile-section h2 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1.5rem;
|
||||
color: #333;
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
}
|
||||
|
||||
/* Profile Details */
|
||||
.hvac-profile-details {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.hvac-detail-row {
|
||||
display: grid;
|
||||
grid-template-columns: 150px 1fr;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hvac-detail-label {
|
||||
font-weight: 600;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.hvac-detail-value {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.hvac-detail-value a {
|
||||
color: #0274be;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.hvac-detail-value a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Profile Bio */
|
||||
.hvac-profile-bio {
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Certifications List */
|
||||
.hvac-certifications-list {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.hvac-certification-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.hvac-certification-item .dashicons {
|
||||
color: #0274be;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* Edit Form Styles */
|
||||
.hvac-form {
|
||||
background-color: white;
|
||||
padding: 2rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.hvac-form-section {
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.hvac-form-section:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.hvac-form-section h3 {
|
||||
margin-bottom: 1.5rem;
|
||||
color: #333;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.hvac-form-row {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.hvac-form-row label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.hvac-form-row input,
|
||||
.hvac-form-row select,
|
||||
.hvac-form-row textarea {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.hvac-form-row input:focus,
|
||||
.hvac-form-row select:focus,
|
||||
.hvac-form-row textarea:focus {
|
||||
outline: none;
|
||||
border-color: #0274be;
|
||||
box-shadow: 0 0 0 3px rgba(2, 116, 190, 0.1);
|
||||
}
|
||||
|
||||
.hvac-form-row-half {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.hvac-form-row-half > div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Profile Photo Upload */
|
||||
.hvac-profile-photo-upload {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.hvac-current-photo img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.hvac-photo-placeholder {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
background-color: #f0f0f0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #999;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.hvac-photo-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.hvac-button {
|
||||
display: inline-block;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.hvac-button-primary {
|
||||
background-color: #0274be;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hvac-button-primary:hover {
|
||||
background-color: #005fa3;
|
||||
}
|
||||
|
||||
.hvac-button-secondary {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hvac-button-secondary:hover {
|
||||
background-color: #5a6268;
|
||||
}
|
||||
|
||||
.hvac-button-danger-outline {
|
||||
background-color: transparent;
|
||||
color: #dc3545;
|
||||
border: 1px solid #dc3545;
|
||||
}
|
||||
|
||||
.hvac-button-danger-outline:hover {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Form Actions */
|
||||
.hvac-form-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-top: 2rem;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
/* Messages */
|
||||
.hvac-message {
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 1.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.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 Errors */
|
||||
.hvac-form-error {
|
||||
border-color: #dc3545 !important;
|
||||
}
|
||||
|
||||
.hvac-error-message {
|
||||
display: block;
|
||||
color: #dc3545;
|
||||
font-size: 0.875rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.hvac-trainer-profile-view,
|
||||
.hvac-trainer-profile-edit {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.hvac-page-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.hvac-profile-content {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.hvac-profile-sidebar {
|
||||
flex: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hvac-detail-row {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.hvac-detail-label {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.hvac-form-row-half {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.hvac-profile-photo-upload {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.hvac-photo-actions {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
357
assets/css/hvac-venues.css
Normal file
357
assets/css/hvac-venues.css
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
/**
|
||||
* HVAC Venues Styles
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
* @version 2.0.0
|
||||
*/
|
||||
|
||||
/* Page Layout */
|
||||
.hvac-venues-list,
|
||||
.hvac-venue-manage {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
/* Page Header */
|
||||
.hvac-page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.hvac-page-header h1 {
|
||||
margin: 0;
|
||||
color: #0274be;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
/* Breadcrumb */
|
||||
.hvac-breadcrumb {
|
||||
margin-bottom: 1.5rem;
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.hvac-breadcrumb a {
|
||||
color: #0274be;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.hvac-breadcrumb a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Filters */
|
||||
.hvac-venues-filters {
|
||||
background-color: #f5f5f5;
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.hvac-filter-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.hvac-filter-group {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.hvac-filter-group input,
|
||||
.hvac-filter-group select {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* Table */
|
||||
.hvac-venues-table-wrapper {
|
||||
overflow-x: auto;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.hvac-venues-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background-color: white;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.hvac-venues-table th {
|
||||
background-color: #f8f9fa;
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.hvac-venues-table td {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.hvac-venues-table tbody tr:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.hvac-no-results {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
padding: 3rem !important;
|
||||
}
|
||||
|
||||
/* Badges */
|
||||
.hvac-badge {
|
||||
display: inline-block;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 3px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.hvac-badge-owner {
|
||||
background-color: #e3f2fd;
|
||||
color: #1976d2;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.hvac-button {
|
||||
display: inline-block;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.hvac-button-primary {
|
||||
background-color: #0274be;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hvac-button-primary:hover {
|
||||
background-color: #005fa3;
|
||||
}
|
||||
|
||||
.hvac-button-secondary {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hvac-button-secondary:hover {
|
||||
background-color: #5a6268;
|
||||
}
|
||||
|
||||
.hvac-button-danger {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hvac-button-danger:hover {
|
||||
background-color: #c82333;
|
||||
}
|
||||
|
||||
.hvac-button-small {
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.hvac-text-muted {
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.hvac-pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.hvac-pagination a,
|
||||
.hvac-pagination span {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.hvac-pagination a:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.hvac-pagination .current {
|
||||
background-color: #0274be;
|
||||
color: white;
|
||||
border-color: #0274be;
|
||||
}
|
||||
|
||||
/* Form Styles */
|
||||
.hvac-form {
|
||||
background-color: white;
|
||||
padding: 2rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.hvac-form-section {
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.hvac-form-section:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.hvac-form-section h3 {
|
||||
margin-bottom: 1.5rem;
|
||||
color: #333;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.hvac-form-row {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.hvac-form-row label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.hvac-form-row input,
|
||||
.hvac-form-row select,
|
||||
.hvac-form-row textarea {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.hvac-form-row input:focus,
|
||||
.hvac-form-row select:focus,
|
||||
.hvac-form-row textarea:focus {
|
||||
outline: none;
|
||||
border-color: #0274be;
|
||||
box-shadow: 0 0 0 3px rgba(2, 116, 190, 0.1);
|
||||
}
|
||||
|
||||
.hvac-form-row-half {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.hvac-form-row-half > div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.hvac-form-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-top: 2rem;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.hvac-form-actions .hvac-button-danger {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.hvac-venues-list,
|
||||
.hvac-venue-manage {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.hvac-page-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.hvac-filter-form {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.hvac-filter-group {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hvac-form-row-half {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.hvac-form-actions {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.hvac-form-actions .hvac-button-danger {
|
||||
margin-left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hvac-venues-table {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.hvac-venues-table th,
|
||||
.hvac-venues-table td {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Messages */
|
||||
.hvac-message {
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 1.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.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 Error Styles */
|
||||
.hvac-form-error {
|
||||
border-color: #dc3545 !important;
|
||||
}
|
||||
|
||||
.hvac-error-message {
|
||||
display: block;
|
||||
color: #dc3545;
|
||||
font-size: 0.875rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
364
assets/js/hvac-organizers.js
Normal file
364
assets/js/hvac-organizers.js
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
/**
|
||||
* HVAC Organizers JavaScript
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
* @version 2.0.0
|
||||
*/
|
||||
|
||||
jQuery(document).ready(function($) {
|
||||
// Cache DOM elements
|
||||
const $organizerForm = $('#hvac-organizer-form');
|
||||
const $uploadButton = $('#hvac-upload-logo');
|
||||
const $removeButton = $('#hvac-remove-logo');
|
||||
const $logoIdField = $('#org_logo_id');
|
||||
const $currentLogo = $('.hvac-current-logo');
|
||||
const $deleteButton = $('#hvac-delete-organizer');
|
||||
|
||||
// Form validation
|
||||
function validateOrganizerForm() {
|
||||
let isValid = true;
|
||||
const errors = [];
|
||||
|
||||
// Clear previous errors
|
||||
$('.hvac-form-error').removeClass('hvac-form-error');
|
||||
$('.hvac-error-message').remove();
|
||||
|
||||
// Required fields
|
||||
const requiredFields = [
|
||||
{ id: 'org_name', label: 'Organization Name' },
|
||||
{ id: 'hq_city', label: 'Headquarters City' },
|
||||
{ id: 'hq_state', label: 'Headquarters State/Province' },
|
||||
{ id: 'hq_country', label: 'Headquarters Country' }
|
||||
];
|
||||
|
||||
requiredFields.forEach(field => {
|
||||
const $field = $('#' + field.id);
|
||||
const value = $field.val();
|
||||
|
||||
if (!value || value.trim() === '') {
|
||||
isValid = false;
|
||||
errors.push(field.label + ' is required');
|
||||
$field.addClass('hvac-form-error');
|
||||
$field.after('<span class="hvac-error-message">' + field.label + ' is required</span>');
|
||||
}
|
||||
});
|
||||
|
||||
// Validate email if provided
|
||||
const email = $('#org_email').val();
|
||||
if (email && !isValidEmail(email)) {
|
||||
isValid = false;
|
||||
errors.push('Please enter a valid email address');
|
||||
$('#org_email').addClass('hvac-form-error');
|
||||
$('#org_email').after('<span class="hvac-error-message">Please enter a valid email address</span>');
|
||||
}
|
||||
|
||||
// Validate phone format if provided
|
||||
const phone = $('#org_phone').val();
|
||||
if (phone && !isValidPhone(phone)) {
|
||||
isValid = false;
|
||||
errors.push('Please enter a valid phone number');
|
||||
$('#org_phone').addClass('hvac-form-error');
|
||||
$('#org_phone').after('<span class="hvac-error-message">Please enter a valid phone number</span>');
|
||||
}
|
||||
|
||||
// Validate website URL if provided
|
||||
const website = $('#org_website').val();
|
||||
if (website && !isValidURL(website)) {
|
||||
isValid = false;
|
||||
errors.push('Please enter a valid website URL');
|
||||
$('#org_website').addClass('hvac-form-error');
|
||||
$('#org_website').after('<span class="hvac-error-message">Please enter a valid website URL</span>');
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
// Email validation
|
||||
function isValidEmail(email) {
|
||||
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return re.test(email);
|
||||
}
|
||||
|
||||
// Phone validation
|
||||
function isValidPhone(phone) {
|
||||
const digits = phone.replace(/\D/g, '');
|
||||
return digits.length >= 10 && digits.length <= 15;
|
||||
}
|
||||
|
||||
// URL validation
|
||||
function isValidURL(url) {
|
||||
try {
|
||||
new URL(url);
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Show message
|
||||
function showMessage(message, type = 'success') {
|
||||
const messageClass = type === 'success' ? 'hvac-message-success' : 'hvac-message-error';
|
||||
const $message = $('<div class="hvac-message ' + messageClass + '">' + message + '</div>');
|
||||
|
||||
// Remove any existing messages
|
||||
$('.hvac-message').remove();
|
||||
|
||||
// Add new message
|
||||
$('.hvac-page-header').after($message);
|
||||
|
||||
// Auto-hide success messages after 5 seconds
|
||||
if (type === 'success') {
|
||||
setTimeout(function() {
|
||||
$message.fadeOut(function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Scroll to top
|
||||
$('html, body').animate({
|
||||
scrollTop: $('.hvac-page-header').offset().top - 100
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// Handle organizer form submission
|
||||
if ($organizerForm.length) {
|
||||
$organizerForm.on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Validate form
|
||||
if (!validateOrganizerForm()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable submit button
|
||||
const $submitButton = $organizerForm.find('button[type="submit"]');
|
||||
const originalText = $submitButton.text();
|
||||
$submitButton.prop('disabled', true).text('Saving...');
|
||||
|
||||
// Gather form data
|
||||
const formData = {
|
||||
action: 'hvac_save_organizer',
|
||||
nonce: hvacOrganizers.nonce,
|
||||
organizer_id: $('input[name="organizer_id"]').val(),
|
||||
org_name: $('#org_name').val(),
|
||||
org_description: $('#org_description').val(),
|
||||
hq_city: $('#hq_city').val(),
|
||||
hq_state: $('#hq_state').val(),
|
||||
hq_country: $('#hq_country').val(),
|
||||
org_phone: $('#org_phone').val(),
|
||||
org_email: $('#org_email').val(),
|
||||
org_website: $('#org_website').val(),
|
||||
org_logo_id: $('#org_logo_id').val()
|
||||
};
|
||||
|
||||
// Send AJAX request
|
||||
$.ajax({
|
||||
url: hvacOrganizers.ajax_url,
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showMessage(response.data.message, 'success');
|
||||
|
||||
// If creating new organizer, update form to edit mode
|
||||
if (!formData.organizer_id && response.data.organizer_id) {
|
||||
$('input[name="organizer_id"]').val(response.data.organizer_id);
|
||||
$('.hvac-page-header h1').text('Edit Organizer');
|
||||
$('.hvac-breadcrumb').html('<a href="/trainer/dashboard/">Trainer</a> > <a href="/trainer/organizer/list/">Organizers</a> > Edit');
|
||||
|
||||
// Add delete button if not present
|
||||
if (!$('#hvac-delete-organizer').length) {
|
||||
const deleteButton = '<button type="button" id="hvac-delete-organizer" class="hvac-button hvac-button-danger" data-organizer-id="' + response.data.organizer_id + '">Delete Organizer</button>';
|
||||
$('.hvac-form-actions').append(deleteButton);
|
||||
}
|
||||
}
|
||||
|
||||
// Update button text
|
||||
$submitButton.text('Update Organizer');
|
||||
} else {
|
||||
showMessage(response.data || 'An error occurred while saving the organizer.', 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showMessage('An error occurred. Please try again.', 'error');
|
||||
},
|
||||
complete: function() {
|
||||
// Re-enable submit button
|
||||
$submitButton.prop('disabled', false).text(originalText);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Handle logo upload
|
||||
if ($uploadButton.length) {
|
||||
let mediaUploader;
|
||||
|
||||
$uploadButton.on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// If the media uploader already exists, open it
|
||||
if (mediaUploader) {
|
||||
mediaUploader.open();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the media uploader
|
||||
mediaUploader = wp.media({
|
||||
title: 'Choose Organization Logo',
|
||||
button: {
|
||||
text: 'Use this logo'
|
||||
},
|
||||
multiple: false,
|
||||
library: {
|
||||
type: 'image'
|
||||
}
|
||||
});
|
||||
|
||||
// When an image is selected, run a callback
|
||||
mediaUploader.on('select', function() {
|
||||
const attachment = mediaUploader.state().get('selection').first().toJSON();
|
||||
|
||||
// Update the logo preview
|
||||
$currentLogo.html('<img src="' + attachment.sizes.medium.url + '" alt="Organization logo" />');
|
||||
|
||||
// Update the hidden field
|
||||
$logoIdField.val(attachment.id);
|
||||
|
||||
// Update button text
|
||||
$uploadButton.text('Change Logo');
|
||||
|
||||
// Show remove button if not already visible
|
||||
if (!$('#hvac-remove-logo').length) {
|
||||
const removeBtn = '<button type="button" id="hvac-remove-logo" class="hvac-button hvac-button-danger-outline">Remove Logo</button>';
|
||||
$uploadButton.after(removeBtn);
|
||||
}
|
||||
});
|
||||
|
||||
// Open the media uploader
|
||||
mediaUploader.open();
|
||||
});
|
||||
}
|
||||
|
||||
// Handle logo removal
|
||||
$(document).on('click', '#hvac-remove-logo', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Clear the logo preview
|
||||
$currentLogo.html('<div class="hvac-logo-placeholder-large"><span>No logo uploaded</span></div>');
|
||||
|
||||
// Clear the hidden field
|
||||
$logoIdField.val('');
|
||||
|
||||
// Update button text
|
||||
$uploadButton.text('Upload Logo');
|
||||
|
||||
// Remove the remove button
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
// Handle organizer deletion
|
||||
$(document).on('click', '#hvac-delete-organizer', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const $deleteButton = $(this);
|
||||
const organizerId = $deleteButton.data('organizer-id');
|
||||
|
||||
if (!organizerId) {
|
||||
showMessage('Invalid organizer ID', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Confirm deletion
|
||||
if (!confirm('Are you sure you want to delete this organizer? This action cannot be undone.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable button
|
||||
$deleteButton.prop('disabled', true).text('Deleting...');
|
||||
|
||||
// Send delete request
|
||||
$.ajax({
|
||||
url: hvacOrganizers.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'hvac_delete_organizer',
|
||||
nonce: hvacOrganizers.nonce,
|
||||
organizer_id: organizerId
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showMessage(response.data || 'Organizer deleted successfully.', 'success');
|
||||
|
||||
// Redirect to organizers list after 2 seconds
|
||||
setTimeout(function() {
|
||||
window.location.href = '/trainer/organizer/list/';
|
||||
}, 2000);
|
||||
} else {
|
||||
showMessage(response.data || 'Failed to delete organizer.', 'error');
|
||||
// Re-enable button
|
||||
$deleteButton.prop('disabled', false).text('Delete Organizer');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showMessage('An error occurred. Please try again.', 'error');
|
||||
// Re-enable button
|
||||
$deleteButton.prop('disabled', false).text('Delete Organizer');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Real-time validation
|
||||
$('#org_email').on('blur', function() {
|
||||
const email = $(this).val();
|
||||
$('.hvac-error-message', $(this).parent()).remove();
|
||||
$(this).removeClass('hvac-form-error');
|
||||
|
||||
if (email && !isValidEmail(email)) {
|
||||
$(this).addClass('hvac-form-error');
|
||||
$(this).after('<span class="hvac-error-message">Please enter a valid email address</span>');
|
||||
}
|
||||
});
|
||||
|
||||
$('#org_phone').on('blur', function() {
|
||||
const phone = $(this).val();
|
||||
$('.hvac-error-message', $(this).parent()).remove();
|
||||
$(this).removeClass('hvac-form-error');
|
||||
|
||||
if (phone && !isValidPhone(phone)) {
|
||||
$(this).addClass('hvac-form-error');
|
||||
$(this).after('<span class="hvac-error-message">Please enter a valid phone number</span>');
|
||||
}
|
||||
});
|
||||
|
||||
$('#org_website').on('blur', function() {
|
||||
const website = $(this).val();
|
||||
$('.hvac-error-message', $(this).parent()).remove();
|
||||
$(this).removeClass('hvac-form-error');
|
||||
|
||||
if (website && !isValidURL(website)) {
|
||||
$(this).addClass('hvac-form-error');
|
||||
$(this).after('<span class="hvac-error-message">Please enter a valid website URL</span>');
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-format phone number
|
||||
$('#org_phone').on('input', function() {
|
||||
let value = $(this).val().replace(/\D/g, '');
|
||||
|
||||
if (value.length > 0) {
|
||||
if (value.length <= 3) {
|
||||
value = value;
|
||||
} else if (value.length <= 6) {
|
||||
value = value.slice(0, 3) + '-' + value.slice(3);
|
||||
} else if (value.length <= 10) {
|
||||
value = value.slice(0, 3) + '-' + value.slice(3, 6) + '-' + value.slice(6);
|
||||
} else {
|
||||
value = value.slice(0, 3) + '-' + value.slice(3, 6) + '-' + value.slice(6, 10);
|
||||
}
|
||||
}
|
||||
|
||||
$(this).val(value);
|
||||
});
|
||||
});
|
||||
363
assets/js/hvac-registration.js
Normal file
363
assets/js/hvac-registration.js
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
jQuery(document).ready(function($) {
|
||||
const $countrySelect = $('#user_country');
|
||||
const $stateSelect = $('#user_state');
|
||||
const $stateOtherInput = $('#user_state_other');
|
||||
const $registrationForm = $('#hvac-registration-form');
|
||||
|
||||
// Venue fields
|
||||
const $createVenue = $('input[name="create_venue"]');
|
||||
const $venueDetails = $('#venue-details');
|
||||
const $venueName = $('#venue_name');
|
||||
const $businessName = $('#business_name');
|
||||
const $userCity = $('#user_city');
|
||||
const $venuePhone = $('#venue_phone');
|
||||
const $venueWebsite = $('#venue_website');
|
||||
const $businessPhone = $('#business_phone');
|
||||
const $businessWebsite = $('#business_website');
|
||||
|
||||
// Form validation helpers
|
||||
function showFieldError(fieldId, message) {
|
||||
const $field = $('#' + fieldId);
|
||||
const $existingError = $field.siblings('.error-message');
|
||||
|
||||
if ($existingError.length) {
|
||||
$existingError.text(message);
|
||||
} else {
|
||||
$field.after('<p class="error-message" id="' + fieldId + '_error">' + message + '</p>');
|
||||
}
|
||||
|
||||
$field.addClass('error');
|
||||
}
|
||||
|
||||
function clearFieldError(fieldId) {
|
||||
const $field = $('#' + fieldId);
|
||||
$field.siblings('.error-message').remove();
|
||||
$field.removeClass('error');
|
||||
}
|
||||
|
||||
// Real-time email validation
|
||||
$('#user_email').on('blur', function() {
|
||||
const email = $(this).val();
|
||||
if (email && !isValidEmail(email)) {
|
||||
showFieldError('user_email', 'Please enter a valid email address.');
|
||||
} else {
|
||||
clearFieldError('user_email');
|
||||
}
|
||||
});
|
||||
|
||||
$('#business_email').on('blur', function() {
|
||||
const email = $(this).val();
|
||||
if (email && !isValidEmail(email)) {
|
||||
showFieldError('business_email', 'Please enter a valid business email address.');
|
||||
} else {
|
||||
clearFieldError('business_email');
|
||||
}
|
||||
});
|
||||
|
||||
function isValidEmail(email) {
|
||||
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return re.test(email);
|
||||
}
|
||||
|
||||
// Real-time password validation
|
||||
$('#user_pass').on('input blur', function() {
|
||||
const password = $(this).val();
|
||||
if (password) {
|
||||
const errors = [];
|
||||
if (password.length < 8) {
|
||||
errors.push('at least 8 characters');
|
||||
}
|
||||
if (!/[A-Z]/.test(password)) {
|
||||
errors.push('one uppercase letter');
|
||||
}
|
||||
if (!/[a-z]/.test(password)) {
|
||||
errors.push('one lowercase letter');
|
||||
}
|
||||
if (!/[0-9]/.test(password)) {
|
||||
errors.push('one number');
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
showFieldError('user_pass', 'Password must contain ' + errors.join(', ') + '.');
|
||||
} else {
|
||||
clearFieldError('user_pass');
|
||||
// Check confirm password if it has a value
|
||||
const confirmPass = $('#confirm_password').val();
|
||||
if (confirmPass) {
|
||||
$('#confirm_password').trigger('blur');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Confirm password validation
|
||||
$('#confirm_password').on('blur', function() {
|
||||
const password = $('#user_pass').val();
|
||||
const confirmPassword = $(this).val();
|
||||
|
||||
if (confirmPassword && password !== confirmPassword) {
|
||||
showFieldError('confirm_password', 'Passwords do not match.');
|
||||
} else {
|
||||
clearFieldError('confirm_password');
|
||||
}
|
||||
});
|
||||
|
||||
// URL validation for optional fields
|
||||
function isValidURL(url) {
|
||||
try {
|
||||
new URL(url);
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$('#user_url, #user_linkedin, #business_website, #venue_website').on('blur', function() {
|
||||
const url = $(this).val();
|
||||
const fieldId = $(this).attr('id');
|
||||
|
||||
if (url && !isValidURL(url)) {
|
||||
const fieldName = fieldId === 'user_url' ? 'personal website' :
|
||||
fieldId === 'user_linkedin' ? 'LinkedIn profile' :
|
||||
fieldId === 'business_website' ? 'organization website' :
|
||||
'venue website';
|
||||
showFieldError(fieldId, 'Please enter a valid URL for your ' + fieldName + '.');
|
||||
} else {
|
||||
clearFieldError(fieldId);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle venue creation toggle
|
||||
$createVenue.on('change', function() {
|
||||
if ($(this).val() === 'Yes') {
|
||||
$venueDetails.slideDown();
|
||||
// Auto-populate venue name if empty
|
||||
updateVenueName();
|
||||
// Auto-populate venue phone and website
|
||||
if (!$venuePhone.val() && $businessPhone.val()) {
|
||||
$venuePhone.val($businessPhone.val());
|
||||
}
|
||||
if (!$venueWebsite.val() && $businessWebsite.val()) {
|
||||
$venueWebsite.val($businessWebsite.val());
|
||||
}
|
||||
} else {
|
||||
$venueDetails.slideUp();
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-populate venue name
|
||||
function updateVenueName() {
|
||||
if (!$venueName.val()) {
|
||||
const businessName = $businessName.val();
|
||||
const city = $userCity.val();
|
||||
if (businessName && city) {
|
||||
$venueName.val(businessName + ' of ' + city);
|
||||
} else if (businessName) {
|
||||
$venueName.val(businessName + ' Training Venue');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update venue name when business name or city changes
|
||||
$businessName.on('blur', updateVenueName);
|
||||
$userCity.on('blur', updateVenueName);
|
||||
|
||||
// Copy organization info to venue fields
|
||||
$businessPhone.on('blur', function() {
|
||||
if (!$venuePhone.val() && $(this).val()) {
|
||||
$venuePhone.val($(this).val());
|
||||
}
|
||||
});
|
||||
|
||||
$businessWebsite.on('blur', function() {
|
||||
if (!$venueWebsite.val() && $(this).val()) {
|
||||
$venueWebsite.val($(this).val());
|
||||
}
|
||||
});
|
||||
|
||||
// Form submission validation
|
||||
$registrationForm.on('submit', function(e) {
|
||||
let hasErrors = false;
|
||||
const errors = [];
|
||||
|
||||
// Check required fields
|
||||
const requiredFields = [
|
||||
{ id: 'user_email', name: 'Email' },
|
||||
{ id: 'user_pass', name: 'Password' },
|
||||
{ id: 'confirm_password', name: 'Confirm Password' },
|
||||
{ id: 'first_name', name: 'First Name' },
|
||||
{ id: 'last_name', name: 'Last Name' },
|
||||
{ id: 'display_name', name: 'Display Name' },
|
||||
{ id: 'description', name: 'Biographical Info' },
|
||||
{ id: 'business_name', name: 'Organization Name' },
|
||||
{ id: 'business_phone', name: 'Organization Phone' },
|
||||
{ id: 'business_email', name: 'Organization Email' },
|
||||
{ id: 'business_description', name: 'Organization Description' },
|
||||
{ id: 'application_details', name: 'Application Details' }
|
||||
];
|
||||
|
||||
// Check venue fields if creating venue
|
||||
if ($('input[name="create_venue"]:checked').val() === 'Yes') {
|
||||
requiredFields.push(
|
||||
{ id: 'venue_name', name: 'Venue Name' },
|
||||
{ id: 'venue_address', name: 'Street Address' },
|
||||
{ id: 'user_country', name: 'Country' },
|
||||
{ id: 'user_city', name: 'City' },
|
||||
{ id: 'user_zip', name: 'Zip/Postal Code' }
|
||||
);
|
||||
}
|
||||
|
||||
requiredFields.forEach(field => {
|
||||
const $field = $('#' + field.id);
|
||||
if (!$field.val() || $field.val().trim() === '') {
|
||||
hasErrors = true;
|
||||
errors.push(field.name + ' is required.');
|
||||
showFieldError(field.id, field.name + ' is required.');
|
||||
}
|
||||
});
|
||||
|
||||
// Check org logo
|
||||
const $orgLogo = $('#org_logo');
|
||||
if ($orgLogo.length && !$orgLogo[0].files.length) {
|
||||
hasErrors = true;
|
||||
errors.push('Organization Logo is required.');
|
||||
showFieldError('org_logo', 'Organization Logo is required.');
|
||||
}
|
||||
|
||||
// Check state/province for venue
|
||||
if ($('input[name="create_venue"]:checked').val() === 'Yes') {
|
||||
const country = $('#user_country').val();
|
||||
if (country) {
|
||||
if (country === 'United States' || country === 'Canada') {
|
||||
const state = $('#user_state').val();
|
||||
if (!state || state === '') {
|
||||
hasErrors = true;
|
||||
errors.push('State/Province is required.');
|
||||
showFieldError('user_state', 'State/Province is required.');
|
||||
}
|
||||
} else {
|
||||
const otherState = $('#user_state_other').val();
|
||||
if (!otherState || otherState.trim() === '') {
|
||||
hasErrors = true;
|
||||
errors.push('State/Province is required.');
|
||||
showFieldError('user_state_other', 'State/Province is required.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check radio buttons
|
||||
if (!$('input[name="create_venue"]:checked').length) {
|
||||
hasErrors = true;
|
||||
errors.push('Please select whether to create a training venue profile.');
|
||||
}
|
||||
|
||||
if (!$('input[name="business_type"]:checked').length) {
|
||||
hasErrors = true;
|
||||
errors.push('Business Type is required.');
|
||||
}
|
||||
|
||||
// Check checkbox groups
|
||||
const checkboxGroups = [
|
||||
{ name: 'training_audience[]', label: 'Training Audience' },
|
||||
{ name: 'training_formats[]', label: 'Training Formats' },
|
||||
{ name: 'training_locations[]', label: 'Training Locations' },
|
||||
{ name: 'training_resources[]', label: 'Training Resources' }
|
||||
];
|
||||
|
||||
checkboxGroups.forEach(group => {
|
||||
if (!$('input[name="' + group.name + '"]:checked').length) {
|
||||
hasErrors = true;
|
||||
errors.push('Please select at least one option for ' + group.label + '.');
|
||||
}
|
||||
});
|
||||
|
||||
if (hasErrors) {
|
||||
e.preventDefault();
|
||||
|
||||
// Show error summary at the top
|
||||
let $errorSummary = $('.hvac-form-errors');
|
||||
if (!$errorSummary.length) {
|
||||
$errorSummary = $('<div class="hvac-form-errors" role="alert"><h3>Please correct the following errors:</h3><ul></ul></div>');
|
||||
$registrationForm.prepend($errorSummary);
|
||||
}
|
||||
|
||||
const $errorList = $errorSummary.find('ul');
|
||||
$errorList.empty();
|
||||
errors.forEach(error => {
|
||||
$errorList.append('<li>' + error + '</li>');
|
||||
});
|
||||
|
||||
// Scroll to top of form
|
||||
$('html, body').animate({
|
||||
scrollTop: $('.hvac-registration-form').offset().top - 100
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// Function to populate states/provinces
|
||||
function loadStates(country) {
|
||||
console.log(`Loading states/provinces for ${country}`); // Keep log for debugging
|
||||
$stateSelect.find('option').not('[value=""],[value="Other"]').remove(); // Clear existing options except defaults
|
||||
|
||||
let options = {};
|
||||
if (country === 'United States' && typeof hvacRegistrationData !== 'undefined' && hvacRegistrationData.states) {
|
||||
options = hvacRegistrationData.states;
|
||||
} else if (country === 'Canada' && typeof hvacRegistrationData !== 'undefined' && hvacRegistrationData.provinces) {
|
||||
options = hvacRegistrationData.provinces;
|
||||
} else {
|
||||
// If country is not US/CA or data is missing, ensure 'Other' is selected and input shown
|
||||
$stateSelect.val('Other').trigger('change'); // Trigger change to show 'Other' input if needed
|
||||
return;
|
||||
}
|
||||
|
||||
// Append new options
|
||||
$.each(options, function(value, label) {
|
||||
// Append before the 'Other' option if it exists, otherwise just append
|
||||
const $otherOption = $stateSelect.find('option[value="Other"]');
|
||||
const $newOption = $('<option></option>').val(value).text(label);
|
||||
if ($otherOption.length > 0) {
|
||||
$newOption.insertBefore($otherOption);
|
||||
} else {
|
||||
$stateSelect.append($newOption);
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure the 'Other' input is hidden initially when states/provinces are loaded
|
||||
$stateOtherInput.hide().val('');
|
||||
// Reset state selection to default prompt
|
||||
$stateSelect.val('');
|
||||
}
|
||||
|
||||
// Handle state/province field visibility based on 'Other' selection
|
||||
$stateSelect.change(function() {
|
||||
if ($(this).val() === 'Other') {
|
||||
$stateOtherInput.show().prop('required', true); // Make required if Other is selected
|
||||
} else {
|
||||
$stateOtherInput.hide().val('').prop('required', false); // Hide and make not required
|
||||
}
|
||||
}).trigger('change'); // Trigger on load to set initial visibility
|
||||
|
||||
// Handle country change to show/hide/populate state field
|
||||
$countrySelect.change(function() {
|
||||
const country = $(this).val();
|
||||
|
||||
if (country === 'United States' || country === 'Canada') {
|
||||
loadStates(country);
|
||||
$stateSelect.show().prop('required', true); // Show and require state select
|
||||
$stateOtherInput.prop('required', false); // Ensure 'Other' input is not required initially
|
||||
} else if (country) {
|
||||
// For other countries, hide state select, select 'Other', show/require 'Other' input
|
||||
$stateSelect.hide().val('Other').prop('required', false); // Hide and make not required
|
||||
$stateOtherInput.show().prop('required', true); // Show and require 'Other' input
|
||||
} else {
|
||||
// No country selected
|
||||
$stateSelect.hide().val('').prop('required', false); // Hide and make not required
|
||||
$stateOtherInput.hide().val('').prop('required', false); // Hide and make not required
|
||||
}
|
||||
}).trigger('change'); // Trigger on load to set initial state based on pre-selected country (if any)
|
||||
|
||||
// Initialize venue visibility on load
|
||||
$createVenue.filter(':checked').trigger('change');
|
||||
|
||||
});
|
||||
317
assets/js/hvac-trainer-profile.js
Normal file
317
assets/js/hvac-trainer-profile.js
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
/**
|
||||
* HVAC Trainer Profile JavaScript
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
* @version 2.0.0
|
||||
*/
|
||||
|
||||
jQuery(document).ready(function($) {
|
||||
// Cache DOM elements
|
||||
const $profileForm = $('#hvac-profile-form');
|
||||
const $uploadButton = $('#hvac-upload-photo');
|
||||
const $removeButton = $('#hvac-remove-photo');
|
||||
const $photoIdField = $('#profile_photo_id');
|
||||
const $currentPhoto = $('.hvac-current-photo');
|
||||
|
||||
// Form validation
|
||||
function validateProfileForm() {
|
||||
let isValid = true;
|
||||
const errors = [];
|
||||
|
||||
// Clear previous errors
|
||||
$('.hvac-form-error').removeClass('hvac-form-error');
|
||||
$('.hvac-error-message').remove();
|
||||
|
||||
// Required fields
|
||||
const requiredFields = [
|
||||
{ id: 'first_name', label: 'First Name' },
|
||||
{ id: 'last_name', label: 'Last Name' },
|
||||
{ id: 'display_name', label: 'Display Name' },
|
||||
{ id: 'email', label: 'Email Address' }
|
||||
];
|
||||
|
||||
requiredFields.forEach(field => {
|
||||
const $field = $('#' + field.id);
|
||||
const value = $field.val();
|
||||
|
||||
if (!value || value.trim() === '') {
|
||||
isValid = false;
|
||||
errors.push(field.label + ' is required');
|
||||
$field.addClass('hvac-form-error');
|
||||
$field.after('<span class="hvac-error-message">' + field.label + ' is required</span>');
|
||||
}
|
||||
});
|
||||
|
||||
// Validate email
|
||||
const email = $('#email').val();
|
||||
if (email && !isValidEmail(email)) {
|
||||
isValid = false;
|
||||
errors.push('Please enter a valid email address');
|
||||
$('#email').addClass('hvac-form-error');
|
||||
$('#email').after('<span class="hvac-error-message">Please enter a valid email address</span>');
|
||||
}
|
||||
|
||||
// Validate phone format if provided
|
||||
const phone = $('#phone').val();
|
||||
if (phone && !isValidPhone(phone)) {
|
||||
isValid = false;
|
||||
errors.push('Please enter a valid phone number');
|
||||
$('#phone').addClass('hvac-form-error');
|
||||
$('#phone').after('<span class="hvac-error-message">Please enter a valid phone number</span>');
|
||||
}
|
||||
|
||||
// Validate URLs if provided
|
||||
const website = $('#website').val();
|
||||
if (website && !isValidURL(website)) {
|
||||
isValid = false;
|
||||
errors.push('Please enter a valid website URL');
|
||||
$('#website').addClass('hvac-form-error');
|
||||
$('#website').after('<span class="hvac-error-message">Please enter a valid website URL</span>');
|
||||
}
|
||||
|
||||
const linkedin = $('#linkedin').val();
|
||||
if (linkedin && !isValidURL(linkedin)) {
|
||||
isValid = false;
|
||||
errors.push('Please enter a valid LinkedIn URL');
|
||||
$('#linkedin').addClass('hvac-form-error');
|
||||
$('#linkedin').after('<span class="hvac-error-message">Please enter a valid LinkedIn URL</span>');
|
||||
}
|
||||
|
||||
// Validate years of experience
|
||||
const years = $('#years_experience').val();
|
||||
if (years && (years < 0 || years > 50)) {
|
||||
isValid = false;
|
||||
errors.push('Years of experience must be between 0 and 50');
|
||||
$('#years_experience').addClass('hvac-form-error');
|
||||
$('#years_experience').after('<span class="hvac-error-message">Must be between 0 and 50</span>');
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
// Email validation
|
||||
function isValidEmail(email) {
|
||||
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return re.test(email);
|
||||
}
|
||||
|
||||
// Phone validation
|
||||
function isValidPhone(phone) {
|
||||
const digits = phone.replace(/\D/g, '');
|
||||
return digits.length >= 10 && digits.length <= 15;
|
||||
}
|
||||
|
||||
// URL validation
|
||||
function isValidURL(url) {
|
||||
try {
|
||||
new URL(url);
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Show message
|
||||
function showMessage(message, type = 'success') {
|
||||
const messageClass = type === 'success' ? 'hvac-message-success' : 'hvac-message-error';
|
||||
const $message = $('<div class="hvac-message ' + messageClass + '">' + message + '</div>');
|
||||
|
||||
// Remove any existing messages
|
||||
$('.hvac-message').remove();
|
||||
|
||||
// Add new message
|
||||
$('.hvac-page-header').after($message);
|
||||
|
||||
// Auto-hide success messages after 5 seconds
|
||||
if (type === 'success') {
|
||||
setTimeout(function() {
|
||||
$message.fadeOut(function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Scroll to top
|
||||
$('html, body').animate({
|
||||
scrollTop: $('.hvac-page-header').offset().top - 100
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// Handle profile form submission
|
||||
if ($profileForm.length) {
|
||||
$profileForm.on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Validate form
|
||||
if (!validateProfileForm()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable submit button
|
||||
const $submitButton = $profileForm.find('button[type="submit"]');
|
||||
const originalText = $submitButton.text();
|
||||
$submitButton.prop('disabled', true).text('Saving...');
|
||||
|
||||
// Gather form data
|
||||
const formData = {
|
||||
action: 'hvac_update_profile',
|
||||
nonce: hvacProfile.nonce,
|
||||
first_name: $('#first_name').val(),
|
||||
last_name: $('#last_name').val(),
|
||||
display_name: $('#display_name').val(),
|
||||
email: $('#email').val(),
|
||||
phone: $('#phone').val(),
|
||||
description: $('#description').val(),
|
||||
city: $('#city').val(),
|
||||
state: $('#state').val(),
|
||||
country: $('#country').val(),
|
||||
years_experience: $('#years_experience').val(),
|
||||
certifications: $('#certifications').val(),
|
||||
website: $('#website').val(),
|
||||
linkedin: $('#linkedin').val(),
|
||||
profile_photo_id: $('#profile_photo_id').val()
|
||||
};
|
||||
|
||||
// Send AJAX request
|
||||
$.ajax({
|
||||
url: hvacProfile.ajax_url,
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showMessage(response.data || 'Profile updated successfully.', 'success');
|
||||
} else {
|
||||
showMessage(response.data || 'An error occurred while updating your profile.', 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showMessage('An error occurred. Please try again.', 'error');
|
||||
},
|
||||
complete: function() {
|
||||
// Re-enable submit button
|
||||
$submitButton.prop('disabled', false).text(originalText);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Handle photo upload
|
||||
if ($uploadButton.length) {
|
||||
let mediaUploader;
|
||||
|
||||
$uploadButton.on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// If the media uploader already exists, open it
|
||||
if (mediaUploader) {
|
||||
mediaUploader.open();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the media uploader
|
||||
mediaUploader = wp.media({
|
||||
title: 'Choose Profile Photo',
|
||||
button: {
|
||||
text: 'Use this photo'
|
||||
},
|
||||
multiple: false,
|
||||
library: {
|
||||
type: 'image'
|
||||
}
|
||||
});
|
||||
|
||||
// When an image is selected, run a callback
|
||||
mediaUploader.on('select', function() {
|
||||
const attachment = mediaUploader.state().get('selection').first().toJSON();
|
||||
|
||||
// Update the photo preview
|
||||
$currentPhoto.html('<img src="' + attachment.sizes.thumbnail.url + '" alt="Profile photo" />');
|
||||
|
||||
// Update the hidden field
|
||||
$photoIdField.val(attachment.id);
|
||||
|
||||
// Update button text
|
||||
$uploadButton.text('Change Photo');
|
||||
|
||||
// Show remove button if not already visible
|
||||
if (!$removeButton.length) {
|
||||
const removeBtn = '<button type="button" id="hvac-remove-photo" class="hvac-button hvac-button-danger-outline">Remove Photo</button>';
|
||||
$uploadButton.after(removeBtn);
|
||||
}
|
||||
});
|
||||
|
||||
// Open the media uploader
|
||||
mediaUploader.open();
|
||||
});
|
||||
}
|
||||
|
||||
// Handle photo removal
|
||||
$(document).on('click', '#hvac-remove-photo', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Clear the photo preview
|
||||
$currentPhoto.html('<div class="hvac-photo-placeholder">No photo uploaded</div>');
|
||||
|
||||
// Clear the hidden field
|
||||
$photoIdField.val('');
|
||||
|
||||
// Update button text
|
||||
$uploadButton.text('Upload Photo');
|
||||
|
||||
// Remove the remove button
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
// Real-time validation
|
||||
$('#email').on('blur', function() {
|
||||
const email = $(this).val();
|
||||
$('.hvac-error-message', $(this).parent()).remove();
|
||||
$(this).removeClass('hvac-form-error');
|
||||
|
||||
if (email && !isValidEmail(email)) {
|
||||
$(this).addClass('hvac-form-error');
|
||||
$(this).after('<span class="hvac-error-message">Please enter a valid email address</span>');
|
||||
}
|
||||
});
|
||||
|
||||
$('#phone').on('blur', function() {
|
||||
const phone = $(this).val();
|
||||
$('.hvac-error-message', $(this).parent()).remove();
|
||||
$(this).removeClass('hvac-form-error');
|
||||
|
||||
if (phone && !isValidPhone(phone)) {
|
||||
$(this).addClass('hvac-form-error');
|
||||
$(this).after('<span class="hvac-error-message">Please enter a valid phone number</span>');
|
||||
}
|
||||
});
|
||||
|
||||
$('#website, #linkedin').on('blur', function() {
|
||||
const url = $(this).val();
|
||||
$('.hvac-error-message', $(this).parent()).remove();
|
||||
$(this).removeClass('hvac-form-error');
|
||||
|
||||
if (url && !isValidURL(url)) {
|
||||
$(this).addClass('hvac-form-error');
|
||||
$(this).after('<span class="hvac-error-message">Please enter a valid URL</span>');
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-format phone number
|
||||
$('#phone').on('input', function() {
|
||||
let value = $(this).val().replace(/\D/g, '');
|
||||
|
||||
if (value.length > 0) {
|
||||
if (value.length <= 3) {
|
||||
value = value;
|
||||
} else if (value.length <= 6) {
|
||||
value = value.slice(0, 3) + '-' + value.slice(3);
|
||||
} else if (value.length <= 10) {
|
||||
value = value.slice(0, 3) + '-' + value.slice(3, 6) + '-' + value.slice(6);
|
||||
} else {
|
||||
value = value.slice(0, 3) + '-' + value.slice(3, 6) + '-' + value.slice(6, 10);
|
||||
}
|
||||
}
|
||||
|
||||
$(this).val(value);
|
||||
});
|
||||
});
|
||||
283
assets/js/hvac-venues.js
Normal file
283
assets/js/hvac-venues.js
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
/**
|
||||
* HVAC Venues JavaScript
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
* @version 2.0.0
|
||||
*/
|
||||
|
||||
jQuery(document).ready(function($) {
|
||||
// Cache DOM elements
|
||||
const $venueForm = $('#hvac-venue-form');
|
||||
const $deleteButton = $('#hvac-delete-venue');
|
||||
const $filterForm = $('.hvac-filter-form');
|
||||
|
||||
// Form validation
|
||||
function validateVenueForm() {
|
||||
let isValid = true;
|
||||
const errors = [];
|
||||
|
||||
// Clear previous errors
|
||||
$('.hvac-form-error').removeClass('hvac-form-error');
|
||||
$('.hvac-error-message').remove();
|
||||
|
||||
// Required fields
|
||||
const requiredFields = [
|
||||
{ id: 'venue_name', label: 'Venue Name' },
|
||||
{ id: 'venue_address', label: 'Street Address' },
|
||||
{ id: 'venue_city', label: 'City' },
|
||||
{ id: 'venue_state', label: 'State/Province' },
|
||||
{ id: 'venue_zip', label: 'Zip/Postal Code' },
|
||||
{ id: 'venue_country', label: 'Country' }
|
||||
];
|
||||
|
||||
requiredFields.forEach(field => {
|
||||
const $field = $('#' + field.id);
|
||||
const value = $field.val();
|
||||
|
||||
if (!value || value.trim() === '') {
|
||||
isValid = false;
|
||||
errors.push(field.label + ' is required');
|
||||
$field.addClass('hvac-form-error');
|
||||
$field.after('<span class="hvac-error-message">' + field.label + ' is required</span>');
|
||||
}
|
||||
});
|
||||
|
||||
// Validate phone format if provided
|
||||
const phone = $('#venue_phone').val();
|
||||
if (phone && !isValidPhone(phone)) {
|
||||
isValid = false;
|
||||
errors.push('Please enter a valid phone number');
|
||||
$('#venue_phone').addClass('hvac-form-error');
|
||||
$('#venue_phone').after('<span class="hvac-error-message">Please enter a valid phone number</span>');
|
||||
}
|
||||
|
||||
// Validate website URL if provided
|
||||
const website = $('#venue_website').val();
|
||||
if (website && !isValidURL(website)) {
|
||||
isValid = false;
|
||||
errors.push('Please enter a valid website URL');
|
||||
$('#venue_website').addClass('hvac-form-error');
|
||||
$('#venue_website').after('<span class="hvac-error-message">Please enter a valid website URL</span>');
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
// Phone validation
|
||||
function isValidPhone(phone) {
|
||||
// Remove all non-digits
|
||||
const digits = phone.replace(/\D/g, '');
|
||||
// Check if it's 10 or 11 digits (US/Canada)
|
||||
return digits.length >= 10 && digits.length <= 15;
|
||||
}
|
||||
|
||||
// URL validation
|
||||
function isValidURL(url) {
|
||||
try {
|
||||
new URL(url);
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Show success message
|
||||
function showMessage(message, type = 'success') {
|
||||
const messageClass = type === 'success' ? 'hvac-message-success' : 'hvac-message-error';
|
||||
const $message = $('<div class="hvac-message ' + messageClass + '">' + message + '</div>');
|
||||
|
||||
// Remove any existing messages
|
||||
$('.hvac-message').remove();
|
||||
|
||||
// Add new message
|
||||
$('.hvac-page-header').after($message);
|
||||
|
||||
// Auto-hide success messages after 5 seconds
|
||||
if (type === 'success') {
|
||||
setTimeout(function() {
|
||||
$message.fadeOut(function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Scroll to top
|
||||
$('html, body').animate({
|
||||
scrollTop: $('.hvac-page-header').offset().top - 100
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// Handle venue form submission
|
||||
if ($venueForm.length) {
|
||||
$venueForm.on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Validate form
|
||||
if (!validateVenueForm()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable submit button
|
||||
const $submitButton = $venueForm.find('button[type="submit"]');
|
||||
const originalText = $submitButton.text();
|
||||
$submitButton.prop('disabled', true).text('Saving...');
|
||||
|
||||
// Gather form data
|
||||
const formData = {
|
||||
action: 'hvac_save_venue',
|
||||
nonce: hvacVenues.nonce,
|
||||
venue_id: $('input[name="venue_id"]').val(),
|
||||
venue_name: $('#venue_name').val(),
|
||||
venue_description: $('#venue_description').val(),
|
||||
venue_address: $('#venue_address').val(),
|
||||
venue_city: $('#venue_city').val(),
|
||||
venue_state: $('#venue_state').val(),
|
||||
venue_zip: $('#venue_zip').val(),
|
||||
venue_country: $('#venue_country').val(),
|
||||
venue_phone: $('#venue_phone').val(),
|
||||
venue_website: $('#venue_website').val()
|
||||
};
|
||||
|
||||
// Send AJAX request
|
||||
$.ajax({
|
||||
url: hvacVenues.ajax_url,
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showMessage(response.data.message, 'success');
|
||||
|
||||
// If creating new venue, update form to edit mode
|
||||
if (!formData.venue_id && response.data.venue_id) {
|
||||
$('input[name="venue_id"]').val(response.data.venue_id);
|
||||
$('.hvac-page-header h1').text('Edit Venue');
|
||||
$('.hvac-breadcrumb').html('<a href="/trainer/dashboard/">Trainer</a> > <a href="/trainer/venue/list/">Venues</a> > Edit');
|
||||
|
||||
// Add delete button if not present
|
||||
if (!$('#hvac-delete-venue').length) {
|
||||
const deleteButton = '<button type="button" id="hvac-delete-venue" class="hvac-button hvac-button-danger" data-venue-id="' + response.data.venue_id + '">Delete Venue</button>';
|
||||
$('.hvac-form-actions').append(deleteButton);
|
||||
}
|
||||
}
|
||||
|
||||
// Update button text
|
||||
$submitButton.text('Update Venue');
|
||||
} else {
|
||||
showMessage(response.data || 'An error occurred while saving the venue.', 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showMessage('An error occurred. Please try again.', 'error');
|
||||
},
|
||||
complete: function() {
|
||||
// Re-enable submit button
|
||||
$submitButton.prop('disabled', false).text(originalText);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Handle venue deletion
|
||||
$(document).on('click', '#hvac-delete-venue', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const $deleteButton = $(this);
|
||||
const venueId = $deleteButton.data('venue-id');
|
||||
|
||||
if (!venueId) {
|
||||
showMessage('Invalid venue ID', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Confirm deletion
|
||||
if (!confirm('Are you sure you want to delete this venue? This action cannot be undone.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable button
|
||||
$deleteButton.prop('disabled', true).text('Deleting...');
|
||||
|
||||
// Send delete request
|
||||
$.ajax({
|
||||
url: hvacVenues.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'hvac_delete_venue',
|
||||
nonce: hvacVenues.nonce,
|
||||
venue_id: venueId
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showMessage(response.data || 'Venue deleted successfully.', 'success');
|
||||
|
||||
// Redirect to venues list after 2 seconds
|
||||
setTimeout(function() {
|
||||
window.location.href = '/trainer/venue/list/';
|
||||
}, 2000);
|
||||
} else {
|
||||
showMessage(response.data || 'Failed to delete venue.', 'error');
|
||||
// Re-enable button
|
||||
$deleteButton.prop('disabled', false).text('Delete Venue');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showMessage('An error occurred. Please try again.', 'error');
|
||||
// Re-enable button
|
||||
$deleteButton.prop('disabled', false).text('Delete Venue');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Handle filter form submission
|
||||
if ($filterForm.length) {
|
||||
// Prevent form submission on enter in search field
|
||||
$filterForm.find('input[name="search"]').on('keypress', function(e) {
|
||||
if (e.which === 13) {
|
||||
e.preventDefault();
|
||||
$filterForm.find('button[type="submit"]').click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Real-time validation
|
||||
$('#venue_phone').on('blur', function() {
|
||||
const phone = $(this).val();
|
||||
$('.hvac-error-message', $(this).parent()).remove();
|
||||
$(this).removeClass('hvac-form-error');
|
||||
|
||||
if (phone && !isValidPhone(phone)) {
|
||||
$(this).addClass('hvac-form-error');
|
||||
$(this).after('<span class="hvac-error-message">Please enter a valid phone number</span>');
|
||||
}
|
||||
});
|
||||
|
||||
$('#venue_website').on('blur', function() {
|
||||
const website = $(this).val();
|
||||
$('.hvac-error-message', $(this).parent()).remove();
|
||||
$(this).removeClass('hvac-form-error');
|
||||
|
||||
if (website && !isValidURL(website)) {
|
||||
$(this).addClass('hvac-form-error');
|
||||
$(this).after('<span class="hvac-error-message">Please enter a valid website URL</span>');
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-format phone number
|
||||
$('#venue_phone').on('input', function() {
|
||||
let value = $(this).val().replace(/\D/g, '');
|
||||
|
||||
if (value.length > 0) {
|
||||
if (value.length <= 3) {
|
||||
value = value;
|
||||
} else if (value.length <= 6) {
|
||||
value = value.slice(0, 3) + '-' + value.slice(3);
|
||||
} else if (value.length <= 10) {
|
||||
value = value.slice(0, 3) + '-' + value.slice(3, 6) + '-' + value.slice(6);
|
||||
} else {
|
||||
value = value.slice(0, 3) + '-' + value.slice(3, 6) + '-' + value.slice(6, 10);
|
||||
}
|
||||
}
|
||||
|
||||
$(this).val(value);
|
||||
});
|
||||
});
|
||||
41
docs/trainer_page_refactor_30July2025.md
Normal file
41
docs/trainer_page_refactor_30July2025.md
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
We need to make a few major updates to the plugin.
|
||||
|
||||
We will be refactoring the Trainer Registration Page.
|
||||
1. First, we want to edit the "Personal Information" section at the top of the form to be better aligned with our desired Trainer Profile page (which is associated with the User post type):[
|
||||
- move the "Application Details" question to this section
|
||||
- Ensure that the custom fields in this section are actually created as custom fields in the User post type.
|
||||
2. The Existing "Business Information" section should be renamed to "Training Organization Information". This will align with the TEC post type called "Organizer".
|
||||
- It will list the following pre-existing fields from the TEC plugin ("*" means required): Org Name *, Org Phone *, Org Email *, Org Website, Org Description *, Org Logo * (featured image of the Organizer).
|
||||
- We want to add new custom fields called "Org Headquarters City", "Org Headquarters State", and "Org Headquarters Country",
|
||||
- We also want all custom fields moved from the "Training Information" into this new "Training Organization Information" section. These fields likely do not currently exist as field types, so we need to create them.
|
||||
3. We must modify how "Venue" records are created. A Venue is a special post type from The Events Calendar Plugin, and all users with the HVAC Trainer role should be associated with at least 1 Venue (created during registration). Here's the changes we want in this section:
|
||||
- The "Address Information" extion should be renamed to "Training Venue Information", and it should be moved to the end of the form.
|
||||
- This section already has a question which asks "Create Training Venue Profile?", which is meant to auto-populate the training venue profile. It should default to "Yes".
|
||||
- The following custom fields fields will be conditionally visible when "Yes" is selected, and though most are auto-populated from the Org section above, the user should be able to edit them: Venue Name (defaults to [Org Name] + " of " [Org City]), Street Address, City, State/Province, Country, Postal Code, Phone, Website
|
||||
|
||||
Next, we want to update/add pages which are accessible to logged in trainers.
|
||||
1. "Training Venues" Page (/trainer/venue/list): This page will allow the users to view a list of all training venues, even ones they didn't create. This should be in table format with similar filters/pagination as the dashboard page. If they are the author of a venue, then they there should be an "Edit" button beside that venue in the table.
|
||||
2. "Manage Venue" Page (/trainer/venue/manage): This should have similar functionality to the "Manage Event" page, but will allow the user to create new Venuws or edit Venues which they are the author of, including all default and custom fields related to the Venue post type.
|
||||
3. "Trainer Profile" Page (/trainer/profile and /trainer/profile/edit): This will be where trainers can view all default and custom fields related to the User post type.
|
||||
4. "Training Organizers" Page (/trainer/organizer/list): This should have similar functionality to the "Manage Event" page, but will allow the user to create new Venuws or edit Organizers which they are the author of, including all default and custom fields related to the Organizer post type.
|
||||
5. "Manage Venue" Page (/trainer/organizer/manage): This should have similar functionality to the "Manage Event" page, but will allow the user to create new Venuws or edit Organizers which they are the author of, including all default and custom fields related to the Organizer post type.
|
||||
|
||||
Finally, We should refactor the navigation, URL structure, and add Breadcrumbs.
|
||||
- URL structure should generally align with the menu structure and we should also add breadcrumbs at the top of each page which also match the same patterns (trainer/dashboard/ = Trainer > Events > Dashboard; trainer/certificates/reports = Trainer > Certificates > Reports;
|
||||
- Instead of having a series of custom buttons, we should create a native wordpress "Menu". It should be a Secondary Menu only available to Logged in users with the HVAC Trainer Role, and will only be shown on the custom pages from our custom plugin. As follows:
|
||||
Events
|
||||
├─ Dashboard
|
||||
├─ New Event
|
||||
Certificates
|
||||
├─ Reports
|
||||
├─ New Certificate
|
||||
Customize
|
||||
├─ Personal Profile
|
||||
├─ Training Organizers
|
||||
│ ├─ Add New Organizer (links to the Manage Organizer Page)
|
||||
├─ Training Venues
|
||||
│ ├─ Add New Venue (links to the Manage Venue Page)
|
||||
Logout
|
||||
Help
|
||||
|
||||
Your first task is to perform a detailed review of the existing page structure and ultrathink a plan to acheive the desired changes. Pay special attention to all of the requisite custom fields and ensure that you follow best practices in creating the field and associating them with the appropriate post types. If there are inherent wordpress limitations which prevent you from achieving your directives, inform the user before making your plan.
|
||||
575
includes/class-hvac-organizers.php
Normal file
575
includes/class-hvac-organizers.php
Normal file
|
|
@ -0,0 +1,575 @@
|
|||
<?php
|
||||
/**
|
||||
* HVAC Organizers Management
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
* @since 2.0.0
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* HVAC_Organizers class
|
||||
*/
|
||||
class HVAC_Organizers {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
// Register shortcodes
|
||||
add_shortcode('hvac_trainer_organizers_list', array($this, 'render_organizers_list'));
|
||||
add_shortcode('hvac_trainer_organizer_manage', array($this, 'render_organizer_manage'));
|
||||
|
||||
// Enqueue scripts
|
||||
add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
|
||||
|
||||
// Handle AJAX requests
|
||||
add_action('wp_ajax_hvac_save_organizer', array($this, 'ajax_save_organizer'));
|
||||
add_action('wp_ajax_hvac_delete_organizer', array($this, 'ajax_delete_organizer'));
|
||||
add_action('wp_ajax_hvac_upload_org_logo', array($this, 'ajax_upload_org_logo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts and styles
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
if (is_page('trainer/organizer/list') || is_page('trainer/organizer/manage')) {
|
||||
wp_enqueue_style(
|
||||
'hvac-organizers-style',
|
||||
HVAC_PLUGIN_URL . 'assets/css/hvac-organizers.css',
|
||||
array(),
|
||||
HVAC_PLUGIN_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'hvac-organizers-js',
|
||||
HVAC_PLUGIN_URL . 'assets/js/hvac-organizers.js',
|
||||
array('jquery'),
|
||||
HVAC_PLUGIN_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script('hvac-organizers-js', 'hvacOrganizers', array(
|
||||
'ajax_url' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('hvac_organizers_nonce')
|
||||
));
|
||||
|
||||
// Enqueue media uploader for logo upload
|
||||
if (is_page('trainer/organizer/manage')) {
|
||||
wp_enqueue_media();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render organizers list
|
||||
*/
|
||||
public function render_organizers_list() {
|
||||
if (!is_user_logged_in() || !current_user_can('hvac_trainer')) {
|
||||
return '<p>You must be logged in as a trainer to view this page.</p>';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="hvac-organizers-list">
|
||||
<div class="hvac-page-header">
|
||||
<h1>Training Organizers</h1>
|
||||
<a href="/trainer/organizer/manage/" class="hvac-button hvac-button-primary">Add New Organizer</a>
|
||||
</div>
|
||||
|
||||
<div class="hvac-breadcrumb">
|
||||
<a href="/trainer/dashboard/">Trainer</a> > <a href="/trainer/organizer/list/">Organizers</a> > List
|
||||
</div>
|
||||
|
||||
<?php $this->render_organizers_table(); ?>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render organizers table
|
||||
*/
|
||||
private function render_organizers_table() {
|
||||
$current_user_id = get_current_user_id();
|
||||
|
||||
// Get pagination parameters
|
||||
$page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
|
||||
$per_page = 20;
|
||||
$offset = ($page - 1) * $per_page;
|
||||
|
||||
// Build query
|
||||
$query_args = array(
|
||||
'post_type' => class_exists('Tribe__Events__Main') ? Tribe__Events__Main::ORGANIZER_POST_TYPE : 'tribe_organizer',
|
||||
'posts_per_page' => $per_page,
|
||||
'offset' => $offset,
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
'post_status' => 'publish',
|
||||
'author' => $current_user_id // Only show organizers created by this user
|
||||
);
|
||||
|
||||
// Filter handling
|
||||
if (!empty($_GET['search'])) {
|
||||
$query_args['s'] = sanitize_text_field($_GET['search']);
|
||||
}
|
||||
|
||||
// Get organizers
|
||||
$organizers_query = new WP_Query($query_args);
|
||||
|
||||
// Get total count for pagination
|
||||
$total_organizers = $organizers_query->found_posts;
|
||||
$total_pages = ceil($total_organizers / $per_page);
|
||||
|
||||
?>
|
||||
<div class="hvac-organizers-filters">
|
||||
<form method="get" class="hvac-filter-form">
|
||||
<div class="hvac-filter-row">
|
||||
<div class="hvac-filter-group">
|
||||
<input type="text" name="search" placeholder="Search organizers..."
|
||||
value="<?php echo esc_attr($_GET['search'] ?? ''); ?>" />
|
||||
</div>
|
||||
<div class="hvac-filter-group">
|
||||
<button type="submit" class="hvac-button">Search</button>
|
||||
<a href="/trainer/organizer/list/" class="hvac-button hvac-button-secondary">Clear</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="hvac-organizers-table-wrapper">
|
||||
<table class="hvac-organizers-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Logo</th>
|
||||
<th>Organization Name</th>
|
||||
<th>Headquarters</th>
|
||||
<th>Contact</th>
|
||||
<th>Website</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
if ($organizers_query->have_posts()) {
|
||||
while ($organizers_query->have_posts()) {
|
||||
$organizers_query->the_post();
|
||||
$organizer_id = get_the_ID();
|
||||
|
||||
// Get organizer meta
|
||||
$phone = get_post_meta($organizer_id, '_OrganizerPhone', true);
|
||||
$email = get_post_meta($organizer_id, '_OrganizerEmail', true);
|
||||
$website = get_post_meta($organizer_id, '_OrganizerWebsite', true);
|
||||
|
||||
// Get headquarters location
|
||||
$hq_city = get_post_meta($organizer_id, '_hvac_headquarters_city', true);
|
||||
$hq_state = get_post_meta($organizer_id, '_hvac_headquarters_state', true);
|
||||
$hq_country = get_post_meta($organizer_id, '_hvac_headquarters_country', true);
|
||||
$hq_parts = array_filter(array($hq_city, $hq_state, $hq_country));
|
||||
$headquarters = implode(', ', $hq_parts);
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td class="hvac-org-logo">
|
||||
<?php if (has_post_thumbnail()): ?>
|
||||
<?php the_post_thumbnail('thumbnail'); ?>
|
||||
<?php else: ?>
|
||||
<div class="hvac-logo-placeholder">
|
||||
<span><?php echo esc_html(substr(get_the_title(), 0, 1)); ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<strong><?php the_title(); ?></strong>
|
||||
</td>
|
||||
<td><?php echo esc_html($headquarters ?: 'Not specified'); ?></td>
|
||||
<td>
|
||||
<?php if ($email): ?>
|
||||
<a href="mailto:<?php echo esc_attr($email); ?>"><?php echo esc_html($email); ?></a>
|
||||
<?php endif; ?>
|
||||
<?php if ($phone): ?>
|
||||
<br /><?php echo esc_html($phone); ?>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($website): ?>
|
||||
<a href="<?php echo esc_url($website); ?>" target="_blank">Visit Website</a>
|
||||
<?php else: ?>
|
||||
<span class="hvac-text-muted">Not specified</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<a href="/trainer/organizer/manage/?organizer_id=<?php echo $organizer_id; ?>"
|
||||
class="hvac-button hvac-button-small">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
} else {
|
||||
?>
|
||||
<tr>
|
||||
<td colspan="6" class="hvac-no-results">No organizers found.</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
wp_reset_postdata();
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php if ($total_pages > 1): ?>
|
||||
<div class="hvac-pagination">
|
||||
<?php
|
||||
echo paginate_links(array(
|
||||
'base' => add_query_arg('paged', '%#%'),
|
||||
'format' => '',
|
||||
'current' => $page,
|
||||
'total' => $total_pages,
|
||||
'prev_text' => '« Previous',
|
||||
'next_text' => 'Next »'
|
||||
));
|
||||
?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render organizer manage form
|
||||
*/
|
||||
public function render_organizer_manage() {
|
||||
if (!is_user_logged_in() || !current_user_can('hvac_trainer')) {
|
||||
return '<p>You must be logged in as a trainer to view this page.</p>';
|
||||
}
|
||||
|
||||
$organizer_id = isset($_GET['organizer_id']) ? intval($_GET['organizer_id']) : 0;
|
||||
$organizer = null;
|
||||
|
||||
if ($organizer_id) {
|
||||
$organizer = get_post($organizer_id);
|
||||
|
||||
// Check if user can edit this organizer
|
||||
if (!$organizer || $organizer->post_author != get_current_user_id()) {
|
||||
return '<p>You do not have permission to edit this organizer.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="hvac-organizer-manage">
|
||||
<div class="hvac-page-header">
|
||||
<h1><?php echo $organizer ? 'Edit Organizer' : 'Create New Organizer'; ?></h1>
|
||||
</div>
|
||||
|
||||
<div class="hvac-breadcrumb">
|
||||
<a href="/trainer/dashboard/">Trainer</a> >
|
||||
<a href="/trainer/organizer/list/">Organizers</a> >
|
||||
<?php echo $organizer ? 'Edit' : 'New'; ?>
|
||||
</div>
|
||||
|
||||
<form id="hvac-organizer-form" class="hvac-form">
|
||||
<?php wp_nonce_field('hvac_organizer_manage', 'hvac_organizer_nonce'); ?>
|
||||
<input type="hidden" name="organizer_id" value="<?php echo $organizer_id; ?>" />
|
||||
|
||||
<div class="hvac-form-section">
|
||||
<h3>Organization Logo</h3>
|
||||
<div class="hvac-org-logo-upload">
|
||||
<div class="hvac-current-logo">
|
||||
<?php if ($organizer && has_post_thumbnail($organizer_id)): ?>
|
||||
<?php echo get_the_post_thumbnail($organizer_id, 'medium'); ?>
|
||||
<?php else: ?>
|
||||
<div class="hvac-logo-placeholder-large">
|
||||
<span>No logo uploaded</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="hvac-logo-actions">
|
||||
<button type="button" id="hvac-upload-logo" class="hvac-button hvac-button-secondary">
|
||||
<?php echo ($organizer && has_post_thumbnail($organizer_id)) ? 'Change Logo' : 'Upload Logo'; ?>
|
||||
</button>
|
||||
<?php if ($organizer && has_post_thumbnail($organizer_id)): ?>
|
||||
<button type="button" id="hvac-remove-logo" class="hvac-button hvac-button-danger-outline">
|
||||
Remove Logo
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<input type="hidden" id="org_logo_id" name="org_logo_id"
|
||||
value="<?php echo $organizer ? get_post_thumbnail_id($organizer_id) : ''; ?>" />
|
||||
</div>
|
||||
<p class="hvac-help-text">Recommended size: 300x300px. Maximum file size: 2MB.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-section">
|
||||
<h3>Organization Information</h3>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="org_name">Organization Name *</label>
|
||||
<input type="text" id="org_name" name="org_name" required
|
||||
value="<?php echo $organizer ? esc_attr($organizer->post_title) : ''; ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="org_description">Description</label>
|
||||
<textarea id="org_description" name="org_description" rows="4"><?php
|
||||
echo $organizer ? esc_textarea($organizer->post_content) : '';
|
||||
?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-section">
|
||||
<h3>Headquarters Location</h3>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="hq_city">City *</label>
|
||||
<input type="text" id="hq_city" name="hq_city" required
|
||||
value="<?php echo $organizer ? esc_attr(get_post_meta($organizer_id, '_hvac_headquarters_city', true)) : ''; ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row hvac-form-row-half">
|
||||
<div>
|
||||
<label for="hq_state">State/Province *</label>
|
||||
<input type="text" id="hq_state" name="hq_state" required
|
||||
value="<?php echo $organizer ? esc_attr(get_post_meta($organizer_id, '_hvac_headquarters_state', true)) : ''; ?>" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="hq_country">Country *</label>
|
||||
<select id="hq_country" name="hq_country" required>
|
||||
<?php
|
||||
$current_country = $organizer ? get_post_meta($organizer_id, '_hvac_headquarters_country', true) : 'United States';
|
||||
$countries = array(
|
||||
'United States' => 'United States',
|
||||
'Canada' => 'Canada',
|
||||
'United Kingdom' => 'United Kingdom',
|
||||
'Australia' => 'Australia'
|
||||
);
|
||||
|
||||
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>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-section">
|
||||
<h3>Contact Information</h3>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="org_phone">Phone</label>
|
||||
<input type="tel" id="org_phone" name="org_phone"
|
||||
value="<?php echo $organizer ? esc_attr(get_post_meta($organizer_id, '_OrganizerPhone', true)) : ''; ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="org_email">Email</label>
|
||||
<input type="email" id="org_email" name="org_email"
|
||||
value="<?php echo $organizer ? esc_attr(get_post_meta($organizer_id, '_OrganizerEmail', true)) : ''; ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="org_website">Website</label>
|
||||
<input type="url" id="org_website" name="org_website"
|
||||
value="<?php echo $organizer ? esc_attr(get_post_meta($organizer_id, '_OrganizerWebsite', true)) : ''; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-actions">
|
||||
<button type="submit" class="hvac-button hvac-button-primary">
|
||||
<?php echo $organizer ? 'Update Organizer' : 'Create Organizer'; ?>
|
||||
</button>
|
||||
<a href="/trainer/organizer/list/" class="hvac-button hvac-button-secondary">Cancel</a>
|
||||
|
||||
<?php if ($organizer): ?>
|
||||
<button type="button" id="hvac-delete-organizer" class="hvac-button hvac-button-danger"
|
||||
data-organizer-id="<?php echo $organizer_id; ?>">Delete Organizer</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for saving organizer
|
||||
*/
|
||||
public function ajax_save_organizer() {
|
||||
check_ajax_referer('hvac_organizers_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('hvac_trainer')) {
|
||||
wp_send_json_error('Unauthorized');
|
||||
}
|
||||
|
||||
$organizer_id = isset($_POST['organizer_id']) ? intval($_POST['organizer_id']) : 0;
|
||||
|
||||
// If editing, check ownership
|
||||
if ($organizer_id) {
|
||||
$organizer = get_post($organizer_id);
|
||||
if (!$organizer || $organizer->post_author != get_current_user_id()) {
|
||||
wp_send_json_error('You do not have permission to edit this organizer.');
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare organizer data
|
||||
$organizer_data = array(
|
||||
'Organizer' => sanitize_text_field($_POST['org_name']),
|
||||
'Description' => wp_kses_post($_POST['org_description']),
|
||||
'Phone' => sanitize_text_field($_POST['org_phone']),
|
||||
'Email' => sanitize_email($_POST['org_email']),
|
||||
'Website' => esc_url_raw($_POST['org_website'])
|
||||
);
|
||||
|
||||
if ($organizer_id) {
|
||||
$organizer_data['ID'] = $organizer_id;
|
||||
$result = function_exists('tribe_update_organizer') ?
|
||||
tribe_update_organizer($organizer_id, $organizer_data) :
|
||||
wp_update_post(array(
|
||||
'ID' => $organizer_id,
|
||||
'post_title' => $organizer_data['Organizer'],
|
||||
'post_content' => $organizer_data['Description']
|
||||
));
|
||||
} else {
|
||||
$organizer_data['post_status'] = 'publish';
|
||||
$organizer_data['post_author'] = get_current_user_id();
|
||||
$result = function_exists('tribe_create_organizer') ?
|
||||
tribe_create_organizer($organizer_data) :
|
||||
wp_insert_post(array(
|
||||
'post_type' => 'tribe_organizer',
|
||||
'post_title' => $organizer_data['Organizer'],
|
||||
'post_content' => $organizer_data['Description'],
|
||||
'post_status' => 'publish',
|
||||
'post_author' => get_current_user_id()
|
||||
));
|
||||
}
|
||||
|
||||
if (is_wp_error($result)) {
|
||||
wp_send_json_error($result->get_error_message());
|
||||
}
|
||||
|
||||
// Update custom meta fields
|
||||
$organizer_id = $organizer_id ?: $result;
|
||||
|
||||
update_post_meta($organizer_id, '_hvac_headquarters_city', sanitize_text_field($_POST['hq_city']));
|
||||
update_post_meta($organizer_id, '_hvac_headquarters_state', sanitize_text_field($_POST['hq_state']));
|
||||
update_post_meta($organizer_id, '_hvac_headquarters_country', sanitize_text_field($_POST['hq_country']));
|
||||
|
||||
// Update phone, email, website meta
|
||||
update_post_meta($organizer_id, '_OrganizerPhone', sanitize_text_field($_POST['org_phone']));
|
||||
update_post_meta($organizer_id, '_OrganizerEmail', sanitize_email($_POST['org_email']));
|
||||
update_post_meta($organizer_id, '_OrganizerWebsite', esc_url_raw($_POST['org_website']));
|
||||
|
||||
// Handle logo
|
||||
if (isset($_POST['org_logo_id'])) {
|
||||
$logo_id = intval($_POST['org_logo_id']);
|
||||
if ($logo_id) {
|
||||
set_post_thumbnail($organizer_id, $logo_id);
|
||||
} else {
|
||||
delete_post_thumbnail($organizer_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Update user's organizer_id if this is their first organizer
|
||||
$user_organizer_id = get_user_meta(get_current_user_id(), 'organizer_id', true);
|
||||
if (!$user_organizer_id) {
|
||||
update_user_meta(get_current_user_id(), 'organizer_id', $organizer_id);
|
||||
}
|
||||
|
||||
wp_send_json_success(array(
|
||||
'message' => $organizer_id ? 'Organizer updated successfully.' : 'Organizer created successfully.',
|
||||
'organizer_id' => $organizer_id
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for deleting organizer
|
||||
*/
|
||||
public function ajax_delete_organizer() {
|
||||
check_ajax_referer('hvac_organizers_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('hvac_trainer')) {
|
||||
wp_send_json_error('Unauthorized');
|
||||
}
|
||||
|
||||
$organizer_id = isset($_POST['organizer_id']) ? intval($_POST['organizer_id']) : 0;
|
||||
|
||||
if (!$organizer_id) {
|
||||
wp_send_json_error('Invalid organizer ID');
|
||||
}
|
||||
|
||||
$organizer = get_post($organizer_id);
|
||||
if (!$organizer || $organizer->post_author != get_current_user_id()) {
|
||||
wp_send_json_error('You do not have permission to delete this organizer.');
|
||||
}
|
||||
|
||||
// Check if organizer is being used by any events
|
||||
$events_using_organizer = get_posts(array(
|
||||
'post_type' => class_exists('Tribe__Events__Main') ? Tribe__Events__Main::POSTTYPE : 'tribe_events',
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => '_EventOrganizerID',
|
||||
'value' => $organizer_id,
|
||||
'compare' => '='
|
||||
)
|
||||
),
|
||||
'posts_per_page' => 1
|
||||
));
|
||||
|
||||
if (!empty($events_using_organizer)) {
|
||||
wp_send_json_error('Cannot delete organizer. It is being used by one or more events.');
|
||||
}
|
||||
|
||||
$result = wp_trash_post($organizer_id);
|
||||
|
||||
if ($result) {
|
||||
// If this was the user's primary organizer, clear it
|
||||
$user_organizer_id = get_user_meta(get_current_user_id(), 'organizer_id', true);
|
||||
if ($user_organizer_id == $organizer_id) {
|
||||
delete_user_meta(get_current_user_id(), 'organizer_id');
|
||||
}
|
||||
|
||||
wp_send_json_success('Organizer deleted successfully.');
|
||||
} else {
|
||||
wp_send_json_error('Failed to delete organizer.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for uploading organization logo
|
||||
*/
|
||||
public function ajax_upload_org_logo() {
|
||||
check_ajax_referer('hvac_organizers_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('hvac_trainer')) {
|
||||
wp_send_json_error('Unauthorized');
|
||||
}
|
||||
|
||||
if (!isset($_FILES['org_logo'])) {
|
||||
wp_send_json_error('No file uploaded');
|
||||
}
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/image.php');
|
||||
require_once(ABSPATH . 'wp-admin/includes/file.php');
|
||||
require_once(ABSPATH . 'wp-admin/includes/media.php');
|
||||
|
||||
$attachment_id = media_handle_upload('org_logo', 0);
|
||||
|
||||
if (is_wp_error($attachment_id)) {
|
||||
wp_send_json_error($attachment_id->get_error_message());
|
||||
}
|
||||
|
||||
wp_send_json_success(array(
|
||||
'attachment_id' => $attachment_id,
|
||||
'url' => wp_get_attachment_image_url($attachment_id, 'medium')
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -97,6 +97,9 @@ class HVAC_Plugin {
|
|||
'class-hvac-trainer-status.php',
|
||||
'class-hvac-access-control.php',
|
||||
'class-hvac-registration.php',
|
||||
'class-hvac-venues.php',
|
||||
'class-hvac-trainer-profile-manager.php',
|
||||
'class-hvac-organizers.php',
|
||||
'class-hvac-manage-event.php',
|
||||
'class-hvac-event-summary.php',
|
||||
'class-hvac-trainer-profile.php',
|
||||
|
|
@ -301,6 +304,21 @@ class HVAC_Plugin {
|
|||
new HVAC_Registration();
|
||||
}
|
||||
|
||||
// Initialize venues management
|
||||
if (class_exists('HVAC_Venues')) {
|
||||
new HVAC_Venues();
|
||||
}
|
||||
|
||||
// Initialize trainer profile manager
|
||||
if (class_exists('HVAC_Trainer_Profile_Manager')) {
|
||||
new HVAC_Trainer_Profile_Manager();
|
||||
}
|
||||
|
||||
// Initialize organizers management
|
||||
if (class_exists('HVAC_Organizers')) {
|
||||
new HVAC_Organizers();
|
||||
}
|
||||
|
||||
// Initialize event management
|
||||
if (class_exists('HVAC_Manage_Event')) {
|
||||
new HVAC_Manage_Event();
|
||||
|
|
|
|||
1416
includes/class-hvac-registration.backup.php
Normal file
1416
includes/class-hvac-registration.backup.php
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -136,6 +136,29 @@ class HVAC_Registration {
|
|||
// error_log('[HVAC REG DEBUG] process_registration_submission: Checking if errors is empty. Result: ' . (empty($errors) ? 'Yes' : 'No'));
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Organization Logo Upload
|
||||
$org_logo_data = null;
|
||||
if (isset($_FILES['org_logo']) && $_FILES['org_logo']['error'] !== UPLOAD_ERR_NO_FILE) {
|
||||
if ($_FILES['org_logo']['error'] === UPLOAD_ERR_OK) {
|
||||
if (!is_uploaded_file($_FILES['org_logo']['tmp_name'])) {
|
||||
$errors['org_logo'] = 'File upload error (invalid temp file).';
|
||||
} else {
|
||||
$allowed_types = ['image/jpeg', 'image/png', 'image/gif'];
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mime_type = finfo_file($finfo, $_FILES['org_logo']['tmp_name']);
|
||||
finfo_close($finfo);
|
||||
|
||||
if (!in_array($mime_type, $allowed_types)) {
|
||||
$errors['org_logo'] = 'Invalid file type detected (' . esc_html($mime_type) . '). Please upload a JPG, PNG, or GIF.';
|
||||
} else {
|
||||
$org_logo_data = $_FILES['org_logo'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$errors['org_logo'] = 'There was an error uploading the organization logo. Code: ' . $_FILES['org_logo']['error'];
|
||||
}
|
||||
}
|
||||
// --- End File Upload Handling ---
|
||||
|
||||
// Validate the rest of the form data
|
||||
|
|
@ -145,7 +168,7 @@ class HVAC_Registration {
|
|||
// --- Process if No Errors ---
|
||||
if (empty($errors)) {
|
||||
|
||||
$user_id = $this->create_trainer_account($submitted_data, $profile_image_data);
|
||||
$user_id = $this->create_trainer_account($submitted_data, $profile_image_data, $org_logo_data);
|
||||
|
||||
if (is_wp_error($user_id)) {
|
||||
$errors['account'] = $user_id->get_error_message();
|
||||
|
|
@ -311,6 +334,12 @@ class HVAC_Registration {
|
|||
<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="application_details"><strong>Application Details *</strong></label>
|
||||
<small>Please explain why you want to create a training account on Upskill HVAC.</small>
|
||||
<textarea name="application_details" id="application_details" rows="5" required aria-describedby="application_details_error"><?php echo esc_textarea($data['application_details'] ?? ''); ?></textarea>
|
||||
<?php if (isset($errors['application_details'])) echo '<p class="error-message" id="application_details_error">' . esc_html($errors['application_details']) . '</p>'; ?>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="profile_image">Profile Image (optional)</label>
|
||||
<input type="file" name="profile_image" id="profile_image" accept="image/jpeg,image/png,image/gif" aria-describedby="profile_image_hint profile_image_error">
|
||||
|
|
@ -319,119 +348,71 @@ class HVAC_Registration {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Business Information Section -->
|
||||
<!-- Training Organization Information Section -->
|
||||
<div class="form-section">
|
||||
<h3>Business Information</h3>
|
||||
<h3>Training Organization Information</h3>
|
||||
<div class="form-row">
|
||||
<label for="business_name"><strong>Business Name *</strong></label>
|
||||
<label for="business_name"><strong>Organization Name *</strong></label>
|
||||
<input type="text" name="business_name" id="business_name" value="<?php echo esc_attr($data['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>
|
||||
<label for="business_phone"><strong>Organization Phone *</strong></label>
|
||||
<input type="tel" name="business_phone" id="business_phone" value="<?php echo esc_attr($data['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>
|
||||
<label for="business_email"><strong>Organization Email *</strong></label>
|
||||
<input type="email" name="business_email" id="business_email" value="<?php echo esc_attr($data['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>
|
||||
<label for="business_website">Organization Website (optional)</label>
|
||||
<input type="url" name="business_website" id="business_website" value="<?php echo esc_attr($data['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>
|
||||
<label for="business_description"><strong>Organization Description *</strong></label>
|
||||
<textarea name="business_description" id="business_description" rows="4" required aria-describedby="business_description_error"><?php echo esc_textarea($data['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($data['user_country'] ?? '', 'United States'); ?>>United States</option>
|
||||
<option value="Canada" <?php selected($data['user_country'] ?? '', 'Canada'); ?>>Canada</option>
|
||||
<option value="" disabled>---</option>
|
||||
<?php
|
||||
$countries = $this->get_country_list();
|
||||
foreach ($countries as $code => $name) {
|
||||
if ($code !== 'US' && $code !== 'CA') {
|
||||
echo '<option value="' . esc_attr($name) . '" ' . selected($data['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">
|
||||
<!-- Options loaded by JS -->
|
||||
<option value="">Select State/Province</option>
|
||||
<option value="Other" <?php selected($data['user_state'] ?? '', 'Other'); ?>>Other</option>
|
||||
<?php
|
||||
// Pre-populate selected state if available from transient
|
||||
$selected_state = $data['user_state'] ?? '';
|
||||
if (!empty($selected_state) && $selected_state !== 'Other') {
|
||||
// Determine if it's a US state or CA province based on country or value itself
|
||||
$is_us_state = array_key_exists($selected_state, $this->get_us_states());
|
||||
$is_ca_province = array_key_exists($selected_state, $this->get_canadian_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($data['user_state_other'] ?? ''); ?>"
|
||||
style="<?php echo (($data['user_state'] ?? '') === 'Other' && ($data['user_country'] ?? '') !== 'United States' && ($data['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($data['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($data['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 Venue Section -->
|
||||
<div class="form-section">
|
||||
<h3>Training Venue</h3>
|
||||
|
||||
<!-- Organization Logo -->
|
||||
<div class="form-row">
|
||||
<label id="create_venue_label"><strong>Create Training Venue Profile? *</strong></label>
|
||||
<small>Do you want to create a Training Venue Profile for your business to use when listing your training events? If yes, we will use the address provided above.</small>
|
||||
<div class="radio-group" role="radiogroup" aria-labelledby="create_venue_label">
|
||||
<label><input type="radio" name="create_venue" value="Yes" <?php checked($data['create_venue'] ?? 'No', 'Yes'); ?> required> Yes</label>
|
||||
<label><input type="radio" name="create_venue" value="No" <?php checked($data['create_venue'] ?? 'No', 'No'); ?>> No</label>
|
||||
</div>
|
||||
<?php if (isset($errors['create_venue'])) echo '<p class="error-message">' . esc_html($errors['create_venue']) . '</p>'; ?>
|
||||
<label for="org_logo"><strong>Organization Logo * </strong></label>
|
||||
<input type="file" name="org_logo" id="org_logo" accept="image/jpeg,image/png,image/gif" required aria-describedby="org_logo_hint org_logo_error">
|
||||
<small id="org_logo_hint">Please attach a .jpg, .png, or .gif image. This will be used as your organization's logo.</small>
|
||||
<?php if (isset($errors['org_logo'])) echo '<p class="error-message" id="org_logo_error">' . esc_html($errors['org_logo']) . '</p>'; ?>
|
||||
</div>
|
||||
|
||||
<!-- Headquarters Location -->
|
||||
<div class="form-row">
|
||||
<h4>Organization Headquarters</h4>
|
||||
</div>
|
||||
<div class="form-row form-row-thirds">
|
||||
<div>
|
||||
<label for="org_headquarters_city">Headquarters City</label>
|
||||
<input type="text" name="org_headquarters_city" id="org_headquarters_city" value="<?php echo esc_attr($data['org_headquarters_city'] ?? ''); ?>" aria-describedby="org_headquarters_city_error">
|
||||
<?php if (isset($errors['org_headquarters_city'])) echo '<p class="error-message" id="org_headquarters_city_error">' . esc_html($errors['org_headquarters_city']) . '</p>'; ?>
|
||||
</div>
|
||||
<div>
|
||||
<label for="org_headquarters_state">Headquarters State</label>
|
||||
<input type="text" name="org_headquarters_state" id="org_headquarters_state" value="<?php echo esc_attr($data['org_headquarters_state'] ?? ''); ?>" aria-describedby="org_headquarters_state_error">
|
||||
<?php if (isset($errors['org_headquarters_state'])) echo '<p class="error-message" id="org_headquarters_state_error">' . esc_html($errors['org_headquarters_state']) . '</p>'; ?>
|
||||
</div>
|
||||
<div>
|
||||
<label for="org_headquarters_country">Headquarters Country</label>
|
||||
<input type="text" name="org_headquarters_country" id="org_headquarters_country" value="<?php echo esc_attr($data['org_headquarters_country'] ?? ''); ?>" aria-describedby="org_headquarters_country_error">
|
||||
<?php if (isset($errors['org_headquarters_country'])) echo '<p class="error-message" id="org_headquarters_country_error">' . esc_html($errors['org_headquarters_country']) . '</p>'; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Training Information (moved from previous section) -->
|
||||
<div class="form-row">
|
||||
<h4>Training Capabilities</h4>
|
||||
</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>
|
||||
|
|
@ -514,23 +495,113 @@ class HVAC_Registration {
|
|||
</div>
|
||||
<?php if (isset($errors['training_resources'])) echo '<p class="error-message">' . esc_html($errors['training_resources']) . '</p>'; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Application Details Section -->
|
||||
<div class="form-section">
|
||||
<h3>Application Details</h3>
|
||||
<div class="form-row">
|
||||
<label for="application_details"><strong>Application Details *</strong></label>
|
||||
<small>Please explain why you want to create a training account on Upskill HVAC.</small>
|
||||
<textarea name="application_details" id="application_details" rows="5" required aria-describedby="application_details_error"><?php echo esc_textarea($data['application_details'] ?? ''); ?></textarea>
|
||||
<?php if (isset($errors['application_details'])) echo '<p class="error-message" id="application_details_error">' . esc_html($errors['application_details']) . '</p>'; ?>
|
||||
</div>
|
||||
<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($data['annual_revenue_target'] ?? ''); ?>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Training Venue Information Section -->
|
||||
<div class="form-section">
|
||||
<h3>Training Venue Information</h3>
|
||||
<div class="form-row">
|
||||
<label id="create_venue_label"><strong>Create Training Venue Profile? *</strong></label>
|
||||
<small>Do you want to create a Training Venue Profile for your organization? This will be used when listing your training events.</small>
|
||||
<div class="radio-group" role="radiogroup" aria-labelledby="create_venue_label">
|
||||
<label><input type="radio" name="create_venue" value="Yes" <?php checked($data['create_venue'] ?? 'Yes', 'Yes'); ?> required> Yes</label>
|
||||
<label><input type="radio" name="create_venue" value="No" <?php checked($data['create_venue'] ?? 'Yes', 'No'); ?>> No</label>
|
||||
</div>
|
||||
<?php if (isset($errors['create_venue'])) echo '<p class="error-message">' . esc_html($errors['create_venue']) . '</p>'; ?>
|
||||
</div>
|
||||
|
||||
<div id="venue-details" style="<?php echo ($data['create_venue'] ?? 'Yes') === 'No' ? 'display:none;' : ''; ?>">
|
||||
<div class="form-row">
|
||||
<label for="venue_name"><strong>Venue Name *</strong></label>
|
||||
<input type="text" name="venue_name" id="venue_name" value="<?php echo esc_attr($data['venue_name'] ?? ''); ?>" aria-describedby="venue_name_hint venue_name_error">
|
||||
<small id="venue_name_hint">Defaults to "[Organization Name] of [City]"</small>
|
||||
<?php if (isset($errors['venue_name'])) echo '<p class="error-message" id="venue_name_error">' . esc_html($errors['venue_name']) . '</p>'; ?>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="venue_address"><strong>Street Address *</strong></label>
|
||||
<input type="text" name="venue_address" id="venue_address" value="<?php echo esc_attr($data['venue_address'] ?? ''); ?>" aria-describedby="venue_address_error">
|
||||
<?php if (isset($errors['venue_address'])) echo '<p class="error-message" id="venue_address_error">' . esc_html($errors['venue_address']) . '</p>'; ?>
|
||||
</div>
|
||||
<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($data['user_country'] ?? '', 'United States'); ?>>United States</option>
|
||||
<option value="Canada" <?php selected($data['user_country'] ?? '', 'Canada'); ?>>Canada</option>
|
||||
<option value="" disabled>---</option>
|
||||
<?php
|
||||
$countries = $this->get_country_list();
|
||||
foreach ($countries as $code => $name) {
|
||||
if ($code !== 'US' && $code !== 'CA') {
|
||||
echo '<option value="' . esc_attr($name) . '" ' . selected($data['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">
|
||||
<!-- Options loaded by JS -->
|
||||
<option value="">Select State/Province</option>
|
||||
<option value="Other" <?php selected($data['user_state'] ?? '', 'Other'); ?>>Other</option>
|
||||
<?php
|
||||
// Pre-populate selected state if available from transient
|
||||
$selected_state = $data['user_state'] ?? '';
|
||||
if (!empty($selected_state) && $selected_state !== 'Other') {
|
||||
// Determine if it's a US state or CA province based on country or value itself
|
||||
$is_us_state = array_key_exists($selected_state, $this->get_us_states());
|
||||
$is_ca_province = array_key_exists($selected_state, $this->get_canadian_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($data['user_state_other'] ?? ''); ?>"
|
||||
style="<?php echo (($data['user_state'] ?? '') === 'Other' && ($data['user_country'] ?? '') !== 'United States' && ($data['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($data['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($data['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 class="form-row form-row-half">
|
||||
<div>
|
||||
<label for="venue_phone">Venue Phone</label>
|
||||
<input type="tel" name="venue_phone" id="venue_phone" value="<?php echo esc_attr($data['venue_phone'] ?? $data['business_phone'] ?? ''); ?>" aria-describedby="venue_phone_error">
|
||||
<?php if (isset($errors['venue_phone'])) echo '<p class="error-message" id="venue_phone_error">' . esc_html($errors['venue_phone']) . '</p>'; ?>
|
||||
</div>
|
||||
<div>
|
||||
<label for="venue_website">Venue Website</label>
|
||||
<input type="url" name="venue_website" id="venue_website" value="<?php echo esc_attr($data['venue_website'] ?? $data['business_website'] ?? ''); ?>" aria-describedby="venue_website_error">
|
||||
<?php if (isset($errors['venue_website'])) echo '<p class="error-message" id="venue_website_error">' . esc_html($errors['venue_website']) . '</p>'; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- end venue-details -->
|
||||
</div>
|
||||
|
||||
<div class="form-submit">
|
||||
<input type="submit" name="hvac_register" value="Register">
|
||||
|
|
@ -616,6 +687,36 @@ class HVAC_Registration {
|
|||
return $attachment_id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle organization logo upload
|
||||
*
|
||||
* @param int $organizer_id The ID of the organizer post to attach the image to.
|
||||
* @param array $file_data The $_FILES array entry for the uploaded image.
|
||||
* @return int|false Attachment ID on success, false on failure.
|
||||
*/
|
||||
private function handle_org_logo_upload($organizer_id, $file_data) {
|
||||
if (!$organizer_id || empty($file_data) || !isset($file_data['tmp_name']) || $file_data['error'] !== UPLOAD_ERR_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/image.php');
|
||||
require_once(ABSPATH . 'wp-admin/includes/file.php');
|
||||
require_once(ABSPATH . 'wp-admin/includes/media.php');
|
||||
|
||||
// Set up the file for upload - need to temporarily set $_FILES for media_handle_upload
|
||||
$_FILES['org_logo_temp'] = $file_data;
|
||||
$attachment_id = media_handle_upload('org_logo_temp', $organizer_id);
|
||||
unset($_FILES['org_logo_temp']);
|
||||
|
||||
if (is_wp_error($attachment_id)) {
|
||||
return false;
|
||||
} else {
|
||||
// Set as featured image for the organizer
|
||||
set_post_thumbnail($organizer_id, $attachment_id);
|
||||
return $attachment_id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate registration form data
|
||||
|
|
@ -636,25 +737,48 @@ class HVAC_Registration {
|
|||
'last_name' => 'Last Name',
|
||||
'display_name' => 'Display Name',
|
||||
'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_name' => 'Organization Name',
|
||||
'business_phone' => 'Organization Phone',
|
||||
'business_email' => 'Organization Email',
|
||||
'business_description' => 'Organization Description',
|
||||
'org_logo' => 'Organization Logo',
|
||||
'create_venue' => 'Create Training Venue Profile selection',
|
||||
'business_type' => 'Business Type',
|
||||
'application_details' => 'Application Details',
|
||||
];
|
||||
|
||||
foreach ($required_fields as $field => $label) {
|
||||
// Special handling for file upload
|
||||
if ($field === 'org_logo') {
|
||||
if (!isset($_FILES['org_logo']) || $_FILES['org_logo']['error'] === UPLOAD_ERR_NO_FILE) {
|
||||
$errors[$field] = $label . ' is required.';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use trim to catch spaces-only input
|
||||
if (empty($data[$field]) || trim($data[$field]) === '') {
|
||||
$errors[$field] = $label . ' is required.';
|
||||
}
|
||||
}
|
||||
|
||||
// Conditional venue fields validation
|
||||
if (isset($data['create_venue']) && $data['create_venue'] === 'Yes') {
|
||||
$venue_required_fields = [
|
||||
'venue_name' => 'Venue Name',
|
||||
'venue_address' => 'Street Address',
|
||||
'user_country' => 'Country',
|
||||
'user_state' => 'State/Province',
|
||||
'user_city' => 'City',
|
||||
'user_zip' => 'Zip/Postal Code',
|
||||
];
|
||||
|
||||
foreach ($venue_required_fields as $field => $label) {
|
||||
if (empty($data[$field]) || trim($data[$field]) === '') {
|
||||
$errors[$field] = $label . ' is required for venue creation.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Required checkbox groups
|
||||
$required_checkboxes = [
|
||||
|
|
@ -718,7 +842,7 @@ class HVAC_Registration {
|
|||
}
|
||||
|
||||
// State/Province 'Other' validation
|
||||
if (!empty($data['user_country'])) {
|
||||
if (!empty($data['user_country']) && isset($data['create_venue']) && $data['create_venue'] === 'Yes') {
|
||||
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') {
|
||||
|
|
@ -752,9 +876,10 @@ class HVAC_Registration {
|
|||
*
|
||||
* @param array $data Sanitized form data.
|
||||
* @param array|null $profile_image_data The $_FILES entry for the profile image, if provided.
|
||||
* @param array|null $org_logo_data The $_FILES entry for the organization logo, if provided.
|
||||
* @return int|WP_Error User ID on success, WP_Error on failure.
|
||||
*/
|
||||
private function create_trainer_account($data, $profile_image_data = null) {
|
||||
private function create_trainer_account($data, $profile_image_data = null, $org_logo_data = null) {
|
||||
// Assume data is already somewhat validated by validate_registration
|
||||
// Perform final sanitization here before insertion
|
||||
$user_email = sanitize_email($data['user_email']);
|
||||
|
|
@ -812,11 +937,9 @@ class HVAC_Registration {
|
|||
'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']),
|
||||
// Use the 'Other' field value if state was 'Other', otherwise use the selected state
|
||||
'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']),
|
||||
'org_headquarters_city' => !empty($data['org_headquarters_city']) ? sanitize_text_field($data['org_headquarters_city']) : '',
|
||||
'org_headquarters_state' => !empty($data['org_headquarters_state']) ? sanitize_text_field($data['org_headquarters_state']) : '',
|
||||
'org_headquarters_country' => !empty($data['org_headquarters_country']) ? sanitize_text_field($data['org_headquarters_country']) : '',
|
||||
'create_venue' => sanitize_text_field($data['create_venue']), // Should be 'Yes' or 'No'
|
||||
'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']) : [],
|
||||
|
|
@ -827,6 +950,19 @@ class HVAC_Registration {
|
|||
'annual_revenue_target' => !empty($data['annual_revenue_target']) ? intval($data['annual_revenue_target']) : '',
|
||||
'account_status' => 'pending' // Set initial status
|
||||
];
|
||||
|
||||
// If venue creation is requested, store venue-specific data
|
||||
if (isset($data['create_venue']) && $data['create_venue'] === 'Yes') {
|
||||
$meta_fields['venue_name'] = !empty($data['venue_name']) ? sanitize_text_field($data['venue_name']) : '';
|
||||
$meta_fields['venue_address'] = !empty($data['venue_address']) ? sanitize_text_field($data['venue_address']) : '';
|
||||
$meta_fields['user_country'] = sanitize_text_field($data['user_country']);
|
||||
// Use the 'Other' field value if state was 'Other', otherwise use the selected state
|
||||
$meta_fields['user_state'] = ($data['user_state'] === 'Other' && isset($data['user_state_other'])) ? sanitize_text_field($data['user_state_other']) : sanitize_text_field($data['user_state']);
|
||||
$meta_fields['user_city'] = sanitize_text_field($data['user_city']);
|
||||
$meta_fields['user_zip'] = sanitize_text_field($data['user_zip']);
|
||||
$meta_fields['venue_phone'] = !empty($data['venue_phone']) ? sanitize_text_field($data['venue_phone']) : '';
|
||||
$meta_fields['venue_website'] = !empty($data['venue_website']) ? esc_url_raw($data['venue_website']) : '';
|
||||
}
|
||||
|
||||
foreach ($meta_fields as $key => $value) {
|
||||
update_user_meta($user_id, $key, $value);
|
||||
|
|
@ -842,7 +978,7 @@ class HVAC_Registration {
|
|||
|
||||
// --- Create Organizer Profile ---
|
||||
|
||||
$organizer_id = $this->create_organizer_profile($user_id, $meta_fields); // Pass sanitized meta fields
|
||||
$organizer_id = $this->create_organizer_profile($user_id, $meta_fields, $org_logo_data); // Pass sanitized meta fields
|
||||
if ($organizer_id) {
|
||||
|
||||
update_user_meta($user_id, 'hvac_organizer_id', $organizer_id);
|
||||
|
|
@ -878,9 +1014,10 @@ class HVAC_Registration {
|
|||
*
|
||||
* @param int $user_id The user ID.
|
||||
* @param array $meta_data Array of sanitized user meta data.
|
||||
* @param array|null $org_logo_data The $_FILES entry for the organization logo, if provided.
|
||||
* @return int|false Organizer Post ID on success, false on failure.
|
||||
*/
|
||||
private function create_organizer_profile($user_id, $meta_data) {
|
||||
private function create_organizer_profile($user_id, $meta_data, $org_logo_data = null) {
|
||||
if (!class_exists('Tribe__Events__Main') || !function_exists('tribe_create_organizer')) {
|
||||
|
||||
return false;
|
||||
|
|
@ -917,6 +1054,33 @@ class HVAC_Registration {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store custom fields as post meta
|
||||
if ($organizer_id) {
|
||||
// Store headquarters location
|
||||
if (!empty($meta_data['org_headquarters_city'])) {
|
||||
update_post_meta($organizer_id, '_hvac_org_headquarters_city', $meta_data['org_headquarters_city']);
|
||||
}
|
||||
if (!empty($meta_data['org_headquarters_state'])) {
|
||||
update_post_meta($organizer_id, '_hvac_org_headquarters_state', $meta_data['org_headquarters_state']);
|
||||
}
|
||||
if (!empty($meta_data['org_headquarters_country'])) {
|
||||
update_post_meta($organizer_id, '_hvac_org_headquarters_country', $meta_data['org_headquarters_country']);
|
||||
}
|
||||
|
||||
// Store training capabilities
|
||||
update_post_meta($organizer_id, '_hvac_business_type', $meta_data['business_type']);
|
||||
update_post_meta($organizer_id, '_hvac_training_audience', $meta_data['training_audience']);
|
||||
update_post_meta($organizer_id, '_hvac_training_formats', $meta_data['training_formats']);
|
||||
update_post_meta($organizer_id, '_hvac_training_locations', $meta_data['training_locations']);
|
||||
update_post_meta($organizer_id, '_hvac_training_resources', $meta_data['training_resources']);
|
||||
update_post_meta($organizer_id, '_hvac_annual_revenue_target', $meta_data['annual_revenue_target']);
|
||||
|
||||
// Handle logo upload
|
||||
if ($org_logo_data) {
|
||||
$this->handle_org_logo_upload($organizer_id, $org_logo_data);
|
||||
}
|
||||
}
|
||||
|
||||
return (int) $organizer_id;
|
||||
}
|
||||
|
|
@ -936,18 +1100,21 @@ class HVAC_Registration {
|
|||
|
||||
// Use the already processed state/province from meta
|
||||
$state_province = $meta_data['user_state'];
|
||||
|
||||
// Determine venue name
|
||||
$venue_name = !empty($meta_data['venue_name']) ? $meta_data['venue_name'] : $meta_data['business_name'] . ' of ' . $meta_data['user_city'];
|
||||
|
||||
$venue_data = array(
|
||||
'Venue' => $meta_data['business_name'] . ' Training Venue', // Venue name from sanitized meta
|
||||
'Venue' => $venue_name,
|
||||
'Country' => $meta_data['user_country'],
|
||||
'Address' => '', // TEC doesn't have a single address line, use City/State/Zip
|
||||
'Address' => $meta_data['venue_address'], // Now we have a specific address field
|
||||
'City' => $meta_data['user_city'],
|
||||
'StateProvince' => $state_province,
|
||||
'State' => $state_province, // Also set State field
|
||||
'Province' => $state_province, // Also set Province field
|
||||
'Zip' => $meta_data['user_zip'],
|
||||
'Phone' => $meta_data['business_phone'],
|
||||
'Website' => $meta_data['business_website'],
|
||||
'Phone' => !empty($meta_data['venue_phone']) ? $meta_data['venue_phone'] : $meta_data['business_phone'],
|
||||
'Website' => !empty($meta_data['venue_website']) ? $meta_data['venue_website'] : $meta_data['business_website'],
|
||||
'post_status' => 'publish', // Publish venue immediately
|
||||
'post_author' => $user_id // Associate with the new user
|
||||
);
|
||||
|
|
@ -1007,7 +1174,7 @@ class HVAC_Registration {
|
|||
$first_name = $user_info->first_name ?: sanitize_text_field($data['first_name']);
|
||||
$last_name = $user_info->last_name ?: sanitize_text_field($data['last_name']);
|
||||
$message .= "Name: " . $first_name . " " . $last_name . "\n";
|
||||
$message .= "Business Name: " . sanitize_text_field($data['business_name']) . "\n"; // Use raw data as meta might not be fully updated yet? Safer to use raw.
|
||||
$message .= "Organization Name: " . sanitize_text_field($data['business_name']) . "\n"; // Use raw data as meta might not be fully updated yet? Safer to use raw.
|
||||
$message .= "Application Details:\n" . wp_kses_post($data['application_details']) . "\n\n"; // Use wp_kses_post for safety
|
||||
|
||||
// Add link to user profile in admin
|
||||
|
|
|
|||
537
includes/class-hvac-trainer-profile-manager.php
Normal file
537
includes/class-hvac-trainer-profile-manager.php
Normal file
|
|
@ -0,0 +1,537 @@
|
|||
<?php
|
||||
/**
|
||||
* HVAC Trainer Profile Manager
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
* @since 2.0.0
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* HVAC_Trainer_Profile_Manager class
|
||||
*/
|
||||
class HVAC_Trainer_Profile_Manager {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
// Register shortcodes
|
||||
add_shortcode('hvac_trainer_profile_view', array($this, 'render_profile_view'));
|
||||
add_shortcode('hvac_trainer_profile_edit', array($this, 'render_profile_edit'));
|
||||
|
||||
// Enqueue scripts
|
||||
add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
|
||||
|
||||
// Handle AJAX requests
|
||||
add_action('wp_ajax_hvac_update_profile', array($this, 'ajax_update_profile'));
|
||||
add_action('wp_ajax_hvac_upload_profile_photo', array($this, 'ajax_upload_profile_photo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts and styles
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
if (is_page('trainer/profile') || is_page('trainer/profile/edit')) {
|
||||
wp_enqueue_style(
|
||||
'hvac-trainer-profile-style',
|
||||
HVAC_PLUGIN_URL . 'assets/css/hvac-trainer-profile.css',
|
||||
array(),
|
||||
HVAC_PLUGIN_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'hvac-trainer-profile-js',
|
||||
HVAC_PLUGIN_URL . 'assets/js/hvac-trainer-profile.js',
|
||||
array('jquery'),
|
||||
HVAC_PLUGIN_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script('hvac-trainer-profile-js', 'hvacProfile', array(
|
||||
'ajax_url' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('hvac_profile_nonce')
|
||||
));
|
||||
|
||||
// Enqueue media uploader for profile photo
|
||||
if (is_page('trainer/profile/edit')) {
|
||||
wp_enqueue_media();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render profile view
|
||||
*/
|
||||
public function render_profile_view() {
|
||||
if (!is_user_logged_in() || !current_user_can('hvac_trainer')) {
|
||||
return '<p>You must be logged in as a trainer to view this page.</p>';
|
||||
}
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
$user = get_userdata($user_id);
|
||||
|
||||
// Get user meta
|
||||
$phone = get_user_meta($user_id, 'user_phone', true);
|
||||
$city = get_user_meta($user_id, 'user_city', true);
|
||||
$state = get_user_meta($user_id, 'user_state', true);
|
||||
$country = get_user_meta($user_id, 'user_country', true);
|
||||
$linkedin = get_user_meta($user_id, 'user_linkedin', true);
|
||||
$certifications = get_user_meta($user_id, 'trainer_certifications', true);
|
||||
$years_experience = get_user_meta($user_id, 'years_experience', true);
|
||||
$profile_photo_id = get_user_meta($user_id, 'profile_photo_id', true);
|
||||
|
||||
// Get organization info
|
||||
$organizer_id = get_user_meta($user_id, 'organizer_id', true);
|
||||
$organization = null;
|
||||
if ($organizer_id) {
|
||||
$organization = get_post($organizer_id);
|
||||
}
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="hvac-trainer-profile-view">
|
||||
<div class="hvac-page-header">
|
||||
<h1>Trainer Profile</h1>
|
||||
<a href="/trainer/profile/edit/" class="hvac-button hvac-button-primary">Edit Profile</a>
|
||||
</div>
|
||||
|
||||
<div class="hvac-breadcrumb">
|
||||
<a href="/trainer/dashboard/">Trainer</a> > <a href="/trainer/profile/">Profile</a> > View
|
||||
</div>
|
||||
|
||||
<div class="hvac-profile-content">
|
||||
<div class="hvac-profile-sidebar">
|
||||
<div class="hvac-profile-photo">
|
||||
<?php if ($profile_photo_id): ?>
|
||||
<?php echo wp_get_attachment_image($profile_photo_id, 'medium', false, array('alt' => $user->display_name)); ?>
|
||||
<?php else: ?>
|
||||
<div class="hvac-profile-photo-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="hvac-profile-stats">
|
||||
<div class="hvac-stat-item">
|
||||
<span class="hvac-stat-value"><?php echo $this->get_trainer_event_count($user_id); ?></span>
|
||||
<span class="hvac-stat-label">Events Created</span>
|
||||
</div>
|
||||
<div class="hvac-stat-item">
|
||||
<span class="hvac-stat-value"><?php echo $this->get_trainer_student_count($user_id); ?></span>
|
||||
<span class="hvac-stat-label">Students Trained</span>
|
||||
</div>
|
||||
<?php if ($years_experience): ?>
|
||||
<div class="hvac-stat-item">
|
||||
<span class="hvac-stat-value"><?php echo esc_html($years_experience); ?></span>
|
||||
<span class="hvac-stat-label">Years Experience</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-profile-main">
|
||||
<div class="hvac-profile-section">
|
||||
<h2>Personal Information</h2>
|
||||
<div class="hvac-profile-details">
|
||||
<div class="hvac-detail-row">
|
||||
<span class="hvac-detail-label">Name:</span>
|
||||
<span class="hvac-detail-value"><?php echo esc_html($user->first_name . ' ' . $user->last_name); ?></span>
|
||||
</div>
|
||||
<div class="hvac-detail-row">
|
||||
<span class="hvac-detail-label">Email:</span>
|
||||
<span class="hvac-detail-value"><?php echo esc_html($user->user_email); ?></span>
|
||||
</div>
|
||||
<?php if ($phone): ?>
|
||||
<div class="hvac-detail-row">
|
||||
<span class="hvac-detail-label">Phone:</span>
|
||||
<span class="hvac-detail-value"><?php echo esc_html($phone); ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="hvac-detail-row">
|
||||
<span class="hvac-detail-label">Location:</span>
|
||||
<span class="hvac-detail-value">
|
||||
<?php
|
||||
$location_parts = array_filter(array($city, $state, $country));
|
||||
echo esc_html(implode(', ', $location_parts));
|
||||
?>
|
||||
</span>
|
||||
</div>
|
||||
<?php if ($linkedin): ?>
|
||||
<div class="hvac-detail-row">
|
||||
<span class="hvac-detail-label">LinkedIn:</span>
|
||||
<span class="hvac-detail-value">
|
||||
<a href="<?php echo esc_url($linkedin); ?>" target="_blank">View Profile</a>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($user->description)): ?>
|
||||
<div class="hvac-profile-section">
|
||||
<h2>About</h2>
|
||||
<div class="hvac-profile-bio">
|
||||
<?php echo wp_kses_post(wpautop($user->description)); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($organization): ?>
|
||||
<div class="hvac-profile-section">
|
||||
<h2>Training Organization</h2>
|
||||
<div class="hvac-profile-details">
|
||||
<div class="hvac-detail-row">
|
||||
<span class="hvac-detail-label">Organization:</span>
|
||||
<span class="hvac-detail-value"><?php echo esc_html($organization->post_title); ?></span>
|
||||
</div>
|
||||
<?php
|
||||
$org_city = get_post_meta($organizer_id, '_hvac_headquarters_city', true);
|
||||
$org_state = get_post_meta($organizer_id, '_hvac_headquarters_state', true);
|
||||
$org_country = get_post_meta($organizer_id, '_hvac_headquarters_country', true);
|
||||
if ($org_city || $org_state || $org_country):
|
||||
?>
|
||||
<div class="hvac-detail-row">
|
||||
<span class="hvac-detail-label">Headquarters:</span>
|
||||
<span class="hvac-detail-value">
|
||||
<?php
|
||||
$hq_parts = array_filter(array($org_city, $org_state, $org_country));
|
||||
echo esc_html(implode(', ', $hq_parts));
|
||||
?>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($certifications): ?>
|
||||
<div class="hvac-profile-section">
|
||||
<h2>Certifications</h2>
|
||||
<div class="hvac-certifications-list">
|
||||
<?php
|
||||
$cert_array = is_array($certifications) ? $certifications : explode("\n", $certifications);
|
||||
foreach ($cert_array as $cert):
|
||||
if (trim($cert)):
|
||||
?>
|
||||
<div class="hvac-certification-item">
|
||||
<i class="dashicons dashicons-awards"></i>
|
||||
<?php echo esc_html(trim($cert)); ?>
|
||||
</div>
|
||||
<?php
|
||||
endif;
|
||||
endforeach;
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render profile edit form
|
||||
*/
|
||||
public function render_profile_edit() {
|
||||
if (!is_user_logged_in() || !current_user_can('hvac_trainer')) {
|
||||
return '<p>You must be logged in as a trainer to view this page.</p>';
|
||||
}
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
$user = get_userdata($user_id);
|
||||
|
||||
// Get user meta
|
||||
$phone = get_user_meta($user_id, 'user_phone', true);
|
||||
$city = get_user_meta($user_id, 'user_city', true);
|
||||
$state = get_user_meta($user_id, 'user_state', true);
|
||||
$country = get_user_meta($user_id, 'user_country', true);
|
||||
$linkedin = get_user_meta($user_id, 'user_linkedin', true);
|
||||
$website = $user->user_url;
|
||||
$certifications = get_user_meta($user_id, 'trainer_certifications', true);
|
||||
$years_experience = get_user_meta($user_id, 'years_experience', true);
|
||||
$profile_photo_id = get_user_meta($user_id, 'profile_photo_id', true);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="hvac-trainer-profile-edit">
|
||||
<div class="hvac-page-header">
|
||||
<h1>Edit Profile</h1>
|
||||
</div>
|
||||
|
||||
<div class="hvac-breadcrumb">
|
||||
<a href="/trainer/dashboard/">Trainer</a> > <a href="/trainer/profile/">Profile</a> > Edit
|
||||
</div>
|
||||
|
||||
<form id="hvac-profile-form" class="hvac-form">
|
||||
<?php wp_nonce_field('hvac_profile_edit', 'hvac_profile_nonce'); ?>
|
||||
|
||||
<div class="hvac-form-section">
|
||||
<h3>Profile Photo</h3>
|
||||
<div class="hvac-profile-photo-upload">
|
||||
<div class="hvac-current-photo">
|
||||
<?php if ($profile_photo_id): ?>
|
||||
<?php echo wp_get_attachment_image($profile_photo_id, 'thumbnail'); ?>
|
||||
<?php else: ?>
|
||||
<div class="hvac-photo-placeholder">No photo uploaded</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="hvac-photo-actions">
|
||||
<button type="button" id="hvac-upload-photo" class="hvac-button hvac-button-secondary">
|
||||
<?php echo $profile_photo_id ? 'Change Photo' : 'Upload Photo'; ?>
|
||||
</button>
|
||||
<?php if ($profile_photo_id): ?>
|
||||
<button type="button" id="hvac-remove-photo" class="hvac-button hvac-button-danger-outline">
|
||||
Remove Photo
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<input type="hidden" id="profile_photo_id" name="profile_photo_id" value="<?php echo $profile_photo_id; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-section">
|
||||
<h3>Personal Information</h3>
|
||||
|
||||
<div class="hvac-form-row hvac-form-row-half">
|
||||
<div>
|
||||
<label for="first_name">First Name *</label>
|
||||
<input type="text" id="first_name" name="first_name" required
|
||||
value="<?php echo esc_attr($user->first_name); ?>" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="last_name">Last Name *</label>
|
||||
<input type="text" id="last_name" name="last_name" required
|
||||
value="<?php echo esc_attr($user->last_name); ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="display_name">Display Name *</label>
|
||||
<input type="text" id="display_name" name="display_name" required
|
||||
value="<?php echo esc_attr($user->display_name); ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="email">Email Address *</label>
|
||||
<input type="email" id="email" name="email" required
|
||||
value="<?php echo esc_attr($user->user_email); ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="phone">Phone Number</label>
|
||||
<input type="tel" id="phone" name="phone"
|
||||
value="<?php echo esc_attr($phone); ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="description">Bio / About</label>
|
||||
<textarea id="description" name="description" rows="5"><?php echo esc_textarea($user->description); ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-section">
|
||||
<h3>Location</h3>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="city">City</label>
|
||||
<input type="text" id="city" name="city"
|
||||
value="<?php echo esc_attr($city); ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row hvac-form-row-half">
|
||||
<div>
|
||||
<label for="state">State/Province</label>
|
||||
<input type="text" id="state" name="state"
|
||||
value="<?php echo esc_attr($state); ?>" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="country">Country</label>
|
||||
<select id="country" name="country">
|
||||
<option value="">Select Country</option>
|
||||
<?php
|
||||
$countries = array(
|
||||
'United States' => 'United States',
|
||||
'Canada' => 'Canada',
|
||||
'United Kingdom' => 'United Kingdom',
|
||||
'Australia' => 'Australia'
|
||||
);
|
||||
|
||||
foreach ($countries as $code => $name) {
|
||||
printf(
|
||||
'<option value="%s" %s>%s</option>',
|
||||
esc_attr($code),
|
||||
selected($country, $code, false),
|
||||
esc_html($name)
|
||||
);
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-section">
|
||||
<h3>Professional Information</h3>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="years_experience">Years of Experience</label>
|
||||
<input type="number" id="years_experience" name="years_experience" min="0" max="50"
|
||||
value="<?php echo esc_attr($years_experience); ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="certifications">Certifications (one per line)</label>
|
||||
<textarea id="certifications" name="certifications" rows="5"><?php echo esc_textarea($certifications); ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="website">Website</label>
|
||||
<input type="url" id="website" name="website"
|
||||
value="<?php echo esc_attr($website); ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="linkedin">LinkedIn Profile</label>
|
||||
<input type="url" id="linkedin" name="linkedin"
|
||||
value="<?php echo esc_attr($linkedin); ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-actions">
|
||||
<button type="submit" class="hvac-button hvac-button-primary">Save Changes</button>
|
||||
<a href="/trainer/profile/" class="hvac-button hvac-button-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for updating profile
|
||||
*/
|
||||
public function ajax_update_profile() {
|
||||
check_ajax_referer('hvac_profile_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('hvac_trainer')) {
|
||||
wp_send_json_error('Unauthorized');
|
||||
}
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
// Update user data
|
||||
$user_data = array(
|
||||
'ID' => $user_id,
|
||||
'first_name' => sanitize_text_field($_POST['first_name']),
|
||||
'last_name' => sanitize_text_field($_POST['last_name']),
|
||||
'display_name' => sanitize_text_field($_POST['display_name']),
|
||||
'user_email' => sanitize_email($_POST['email']),
|
||||
'user_url' => esc_url_raw($_POST['website']),
|
||||
'description' => wp_kses_post($_POST['description'])
|
||||
);
|
||||
|
||||
$result = wp_update_user($user_data);
|
||||
|
||||
if (is_wp_error($result)) {
|
||||
wp_send_json_error($result->get_error_message());
|
||||
}
|
||||
|
||||
// Update user meta
|
||||
update_user_meta($user_id, 'user_phone', sanitize_text_field($_POST['phone']));
|
||||
update_user_meta($user_id, 'user_city', sanitize_text_field($_POST['city']));
|
||||
update_user_meta($user_id, 'user_state', sanitize_text_field($_POST['state']));
|
||||
update_user_meta($user_id, 'user_country', sanitize_text_field($_POST['country']));
|
||||
update_user_meta($user_id, 'user_linkedin', esc_url_raw($_POST['linkedin']));
|
||||
update_user_meta($user_id, 'years_experience', intval($_POST['years_experience']));
|
||||
update_user_meta($user_id, 'trainer_certifications', sanitize_textarea_field($_POST['certifications']));
|
||||
|
||||
// Update profile photo if changed
|
||||
if (isset($_POST['profile_photo_id'])) {
|
||||
update_user_meta($user_id, 'profile_photo_id', intval($_POST['profile_photo_id']));
|
||||
}
|
||||
|
||||
wp_send_json_success('Profile updated successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for uploading profile photo
|
||||
*/
|
||||
public function ajax_upload_profile_photo() {
|
||||
check_ajax_referer('hvac_profile_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('hvac_trainer')) {
|
||||
wp_send_json_error('Unauthorized');
|
||||
}
|
||||
|
||||
if (!isset($_FILES['profile_photo'])) {
|
||||
wp_send_json_error('No file uploaded');
|
||||
}
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/image.php');
|
||||
require_once(ABSPATH . 'wp-admin/includes/file.php');
|
||||
require_once(ABSPATH . 'wp-admin/includes/media.php');
|
||||
|
||||
$attachment_id = media_handle_upload('profile_photo', 0);
|
||||
|
||||
if (is_wp_error($attachment_id)) {
|
||||
wp_send_json_error($attachment_id->get_error_message());
|
||||
}
|
||||
|
||||
// Update user meta
|
||||
update_user_meta(get_current_user_id(), 'profile_photo_id', $attachment_id);
|
||||
|
||||
wp_send_json_success(array(
|
||||
'attachment_id' => $attachment_id,
|
||||
'url' => wp_get_attachment_image_url($attachment_id, 'thumbnail')
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trainer event count
|
||||
*/
|
||||
private function get_trainer_event_count($user_id) {
|
||||
$post_type = class_exists('Tribe__Events__Main') ? Tribe__Events__Main::POSTTYPE : 'tribe_events';
|
||||
|
||||
$count = count_user_posts($user_id, $post_type);
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trainer student count
|
||||
*/
|
||||
private function get_trainer_student_count($user_id) {
|
||||
global $wpdb;
|
||||
|
||||
// Get all events by this trainer
|
||||
$post_type = class_exists('Tribe__Events__Main') ? Tribe__Events__Main::POSTTYPE : 'tribe_events';
|
||||
|
||||
$events = get_posts(array(
|
||||
'post_type' => $post_type,
|
||||
'author' => $user_id,
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids'
|
||||
));
|
||||
|
||||
if (empty($events)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Count attendees across all events
|
||||
$attendee_count = 0;
|
||||
foreach ($events as $event_id) {
|
||||
$attendees = get_post_meta($event_id, '_tribe_tickets_attendees', true);
|
||||
if (is_array($attendees)) {
|
||||
$attendee_count += count($attendees);
|
||||
}
|
||||
}
|
||||
|
||||
return $attendee_count;
|
||||
}
|
||||
}
|
||||
536
includes/class-hvac-venues.php
Normal file
536
includes/class-hvac-venues.php
Normal file
|
|
@ -0,0 +1,536 @@
|
|||
<?php
|
||||
/**
|
||||
* HVAC Venues Management
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
* @since 2.0.0
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* HVAC_Venues class
|
||||
*/
|
||||
class HVAC_Venues {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
// Register shortcodes
|
||||
add_shortcode('hvac_trainer_venues_list', array($this, 'render_venues_list'));
|
||||
add_shortcode('hvac_trainer_venue_manage', array($this, 'render_venue_manage'));
|
||||
|
||||
// Enqueue scripts
|
||||
add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
|
||||
|
||||
// Handle AJAX requests
|
||||
add_action('wp_ajax_hvac_save_venue', array($this, 'ajax_save_venue'));
|
||||
add_action('wp_ajax_hvac_delete_venue', array($this, 'ajax_delete_venue'));
|
||||
add_action('wp_ajax_hvac_load_venue', array($this, 'ajax_load_venue'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts and styles
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
if (is_page('trainer/venue/list') || is_page('trainer/venue/manage')) {
|
||||
wp_enqueue_style(
|
||||
'hvac-venues-style',
|
||||
HVAC_PLUGIN_URL . 'assets/css/hvac-venues.css',
|
||||
array(),
|
||||
HVAC_PLUGIN_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'hvac-venues-js',
|
||||
HVAC_PLUGIN_URL . 'assets/js/hvac-venues.js',
|
||||
array('jquery'),
|
||||
HVAC_PLUGIN_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script('hvac-venues-js', 'hvacVenues', array(
|
||||
'ajax_url' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('hvac_venues_nonce')
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render venues list
|
||||
*/
|
||||
public function render_venues_list() {
|
||||
if (!is_user_logged_in() || !current_user_can('hvac_trainer')) {
|
||||
return '<p>You must be logged in as a trainer to view this page.</p>';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="hvac-venues-list">
|
||||
<div class="hvac-page-header">
|
||||
<h1>Training Venues</h1>
|
||||
<a href="/trainer/venue/manage/" class="hvac-button hvac-button-primary">Add New Venue</a>
|
||||
</div>
|
||||
|
||||
<div class="hvac-breadcrumb">
|
||||
<a href="/trainer/dashboard/">Trainer</a> > <a href="/trainer/venue/list/">Venues</a> > List
|
||||
</div>
|
||||
|
||||
<?php $this->render_venues_table(); ?>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render venues table
|
||||
*/
|
||||
private function render_venues_table() {
|
||||
global $wpdb;
|
||||
|
||||
$current_user_id = get_current_user_id();
|
||||
|
||||
// Get pagination parameters
|
||||
$page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
|
||||
$per_page = 20;
|
||||
$offset = ($page - 1) * $per_page;
|
||||
|
||||
// Build query
|
||||
$query_args = array(
|
||||
'post_type' => class_exists('Tribe__Events__Main') ? Tribe__Events__Main::VENUE_POST_TYPE : 'tribe_venue',
|
||||
'posts_per_page' => $per_page,
|
||||
'offset' => $offset,
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
'post_status' => 'publish'
|
||||
);
|
||||
|
||||
// Filter handling
|
||||
if (!empty($_GET['search'])) {
|
||||
$query_args['s'] = sanitize_text_field($_GET['search']);
|
||||
}
|
||||
|
||||
if (!empty($_GET['state'])) {
|
||||
$query_args['meta_query'] = array(
|
||||
array(
|
||||
'key' => '_VenueStateProvince',
|
||||
'value' => sanitize_text_field($_GET['state']),
|
||||
'compare' => '='
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Get venues
|
||||
$venues_query = new WP_Query($query_args);
|
||||
|
||||
// Get total count for pagination
|
||||
$total_venues = $venues_query->found_posts;
|
||||
$total_pages = ceil($total_venues / $per_page);
|
||||
|
||||
?>
|
||||
<div class="hvac-venues-filters">
|
||||
<form method="get" class="hvac-filter-form">
|
||||
<div class="hvac-filter-row">
|
||||
<div class="hvac-filter-group">
|
||||
<input type="text" name="search" placeholder="Search venues..."
|
||||
value="<?php echo esc_attr($_GET['search'] ?? ''); ?>" />
|
||||
</div>
|
||||
<div class="hvac-filter-group">
|
||||
<select name="state">
|
||||
<option value="">All States</option>
|
||||
<?php
|
||||
// Get unique states
|
||||
$states = $wpdb->get_col("
|
||||
SELECT DISTINCT meta_value
|
||||
FROM {$wpdb->postmeta}
|
||||
WHERE meta_key = '_VenueStateProvince'
|
||||
AND meta_value != ''
|
||||
ORDER BY meta_value
|
||||
");
|
||||
|
||||
foreach ($states as $state) {
|
||||
printf(
|
||||
'<option value="%s" %s>%s</option>',
|
||||
esc_attr($state),
|
||||
selected($_GET['state'] ?? '', $state, false),
|
||||
esc_html($state)
|
||||
);
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="hvac-filter-group">
|
||||
<button type="submit" class="hvac-button">Filter</button>
|
||||
<a href="/trainer/venue/list/" class="hvac-button hvac-button-secondary">Clear</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="hvac-venues-table-wrapper">
|
||||
<table class="hvac-venues-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Venue Name</th>
|
||||
<th>Address</th>
|
||||
<th>City</th>
|
||||
<th>State</th>
|
||||
<th>Phone</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
if ($venues_query->have_posts()) {
|
||||
while ($venues_query->have_posts()) {
|
||||
$venues_query->the_post();
|
||||
$venue_id = get_the_ID();
|
||||
$is_author = (get_post_field('post_author', $venue_id) == $current_user_id);
|
||||
|
||||
// Get venue meta
|
||||
$address = get_post_meta($venue_id, '_VenueAddress', true);
|
||||
$city = get_post_meta($venue_id, '_VenueCity', true);
|
||||
$state = get_post_meta($venue_id, '_VenueStateProvince', true);
|
||||
$phone = get_post_meta($venue_id, '_VenuePhone', true);
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?php the_title(); ?></strong>
|
||||
<?php if ($is_author): ?>
|
||||
<span class="hvac-badge hvac-badge-owner">Your Venue</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo esc_html($address); ?></td>
|
||||
<td><?php echo esc_html($city); ?></td>
|
||||
<td><?php echo esc_html($state); ?></td>
|
||||
<td><?php echo esc_html($phone); ?></td>
|
||||
<td>
|
||||
<?php if ($is_author): ?>
|
||||
<a href="/trainer/venue/manage/?venue_id=<?php echo $venue_id; ?>"
|
||||
class="hvac-button hvac-button-small">Edit</a>
|
||||
<?php else: ?>
|
||||
<span class="hvac-text-muted">View Only</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
} else {
|
||||
?>
|
||||
<tr>
|
||||
<td colspan="6" class="hvac-no-results">No venues found.</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
wp_reset_postdata();
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php if ($total_pages > 1): ?>
|
||||
<div class="hvac-pagination">
|
||||
<?php
|
||||
echo paginate_links(array(
|
||||
'base' => add_query_arg('paged', '%#%'),
|
||||
'format' => '',
|
||||
'current' => $page,
|
||||
'total' => $total_pages,
|
||||
'prev_text' => '« Previous',
|
||||
'next_text' => 'Next »'
|
||||
));
|
||||
?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render venue manage form
|
||||
*/
|
||||
public function render_venue_manage() {
|
||||
if (!is_user_logged_in() || !current_user_can('hvac_trainer')) {
|
||||
return '<p>You must be logged in as a trainer to view this page.</p>';
|
||||
}
|
||||
|
||||
$venue_id = isset($_GET['venue_id']) ? intval($_GET['venue_id']) : 0;
|
||||
$venue = null;
|
||||
|
||||
if ($venue_id) {
|
||||
$venue = get_post($venue_id);
|
||||
|
||||
// Check if user can edit this venue
|
||||
if (!$venue || $venue->post_author != get_current_user_id()) {
|
||||
return '<p>You do not have permission to edit this venue.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="hvac-venue-manage">
|
||||
<div class="hvac-page-header">
|
||||
<h1><?php echo $venue ? 'Edit Venue' : 'Create New Venue'; ?></h1>
|
||||
</div>
|
||||
|
||||
<div class="hvac-breadcrumb">
|
||||
<a href="/trainer/dashboard/">Trainer</a> >
|
||||
<a href="/trainer/venue/list/">Venues</a> >
|
||||
<?php echo $venue ? 'Edit' : 'New'; ?>
|
||||
</div>
|
||||
|
||||
<form id="hvac-venue-form" class="hvac-form">
|
||||
<?php wp_nonce_field('hvac_venue_manage', 'hvac_venue_nonce'); ?>
|
||||
<input type="hidden" name="venue_id" value="<?php echo $venue_id; ?>" />
|
||||
|
||||
<div class="hvac-form-section">
|
||||
<h3>Venue Information</h3>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="venue_name">Venue Name *</label>
|
||||
<input type="text" id="venue_name" name="venue_name" required
|
||||
value="<?php echo $venue ? esc_attr($venue->post_title) : ''; ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="venue_description">Description</label>
|
||||
<textarea id="venue_description" name="venue_description" rows="4"><?php
|
||||
echo $venue ? esc_textarea($venue->post_content) : '';
|
||||
?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-section">
|
||||
<h3>Location Details</h3>
|
||||
|
||||
<div class="hvac-form-row">
|
||||
<label for="venue_address">Street Address *</label>
|
||||
<input type="text" id="venue_address" name="venue_address" required
|
||||
value="<?php echo $venue ? esc_attr(get_post_meta($venue_id, '_VenueAddress', true)) : ''; ?>" />
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row hvac-form-row-half">
|
||||
<div>
|
||||
<label for="venue_city">City *</label>
|
||||
<input type="text" id="venue_city" name="venue_city" required
|
||||
value="<?php echo $venue ? esc_attr(get_post_meta($venue_id, '_VenueCity', true)) : ''; ?>" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="venue_state">State/Province *</label>
|
||||
<input type="text" id="venue_state" name="venue_state" required
|
||||
value="<?php echo $venue ? esc_attr(get_post_meta($venue_id, '_VenueStateProvince', true)) : ''; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-row hvac-form-row-half">
|
||||
<div>
|
||||
<label for="venue_zip">Zip/Postal Code *</label>
|
||||
<input type="text" id="venue_zip" name="venue_zip" required
|
||||
value="<?php echo $venue ? esc_attr(get_post_meta($venue_id, '_VenueZip', true)) : ''; ?>" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="venue_country">Country *</label>
|
||||
<select id="venue_country" name="venue_country" required>
|
||||
<?php
|
||||
$current_country = $venue ? get_post_meta($venue_id, '_VenueCountry', true) : 'United States';
|
||||
$countries = array(
|
||||
'United States' => 'United States',
|
||||
'Canada' => 'Canada',
|
||||
'United Kingdom' => 'United Kingdom',
|
||||
'Australia' => 'Australia'
|
||||
);
|
||||
|
||||
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>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-section">
|
||||
<h3>Contact Information</h3>
|
||||
|
||||
<div class="hvac-form-row hvac-form-row-half">
|
||||
<div>
|
||||
<label for="venue_phone">Phone</label>
|
||||
<input type="tel" id="venue_phone" name="venue_phone"
|
||||
value="<?php echo $venue ? esc_attr(get_post_meta($venue_id, '_VenuePhone', true)) : ''; ?>" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="venue_website">Website</label>
|
||||
<input type="url" id="venue_website" name="venue_website"
|
||||
value="<?php echo $venue ? esc_attr(get_post_meta($venue_id, '_VenueURL', true)) : ''; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hvac-form-actions">
|
||||
<button type="submit" class="hvac-button hvac-button-primary">
|
||||
<?php echo $venue ? 'Update Venue' : 'Create Venue'; ?>
|
||||
</button>
|
||||
<a href="/trainer/venue/list/" class="hvac-button hvac-button-secondary">Cancel</a>
|
||||
|
||||
<?php if ($venue): ?>
|
||||
<button type="button" id="hvac-delete-venue" class="hvac-button hvac-button-danger"
|
||||
data-venue-id="<?php echo $venue_id; ?>">Delete Venue</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for saving venue
|
||||
*/
|
||||
public function ajax_save_venue() {
|
||||
check_ajax_referer('hvac_venues_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('hvac_trainer')) {
|
||||
wp_send_json_error('Unauthorized');
|
||||
}
|
||||
|
||||
$venue_id = isset($_POST['venue_id']) ? intval($_POST['venue_id']) : 0;
|
||||
|
||||
// If editing, check ownership
|
||||
if ($venue_id) {
|
||||
$venue = get_post($venue_id);
|
||||
if (!$venue || $venue->post_author != get_current_user_id()) {
|
||||
wp_send_json_error('You do not have permission to edit this venue.');
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare venue data
|
||||
$venue_data = array(
|
||||
'Venue' => sanitize_text_field($_POST['venue_name']),
|
||||
'Description' => wp_kses_post($_POST['venue_description']),
|
||||
'Address' => sanitize_text_field($_POST['venue_address']),
|
||||
'City' => sanitize_text_field($_POST['venue_city']),
|
||||
'StateProvince' => sanitize_text_field($_POST['venue_state']),
|
||||
'State' => sanitize_text_field($_POST['venue_state']),
|
||||
'Province' => sanitize_text_field($_POST['venue_state']),
|
||||
'Zip' => sanitize_text_field($_POST['venue_zip']),
|
||||
'Country' => sanitize_text_field($_POST['venue_country']),
|
||||
'Phone' => sanitize_text_field($_POST['venue_phone']),
|
||||
'URL' => esc_url_raw($_POST['venue_website']),
|
||||
'ShowMap' => true,
|
||||
'ShowMapLink' => true
|
||||
);
|
||||
|
||||
if ($venue_id) {
|
||||
$venue_data['ID'] = $venue_id;
|
||||
$result = function_exists('tribe_update_venue') ?
|
||||
tribe_update_venue($venue_id, $venue_data) :
|
||||
wp_update_post($venue_data);
|
||||
} else {
|
||||
$venue_data['post_status'] = 'publish';
|
||||
$venue_data['post_author'] = get_current_user_id();
|
||||
$result = function_exists('tribe_create_venue') ?
|
||||
tribe_create_venue($venue_data) :
|
||||
wp_insert_post($venue_data);
|
||||
}
|
||||
|
||||
if (is_wp_error($result)) {
|
||||
wp_send_json_error($result->get_error_message());
|
||||
} else {
|
||||
wp_send_json_success(array(
|
||||
'message' => $venue_id ? 'Venue updated successfully.' : 'Venue created successfully.',
|
||||
'venue_id' => $result
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for deleting venue
|
||||
*/
|
||||
public function ajax_delete_venue() {
|
||||
check_ajax_referer('hvac_venues_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('hvac_trainer')) {
|
||||
wp_send_json_error('Unauthorized');
|
||||
}
|
||||
|
||||
$venue_id = isset($_POST['venue_id']) ? intval($_POST['venue_id']) : 0;
|
||||
|
||||
if (!$venue_id) {
|
||||
wp_send_json_error('Invalid venue ID');
|
||||
}
|
||||
|
||||
$venue = get_post($venue_id);
|
||||
if (!$venue || $venue->post_author != get_current_user_id()) {
|
||||
wp_send_json_error('You do not have permission to delete this venue.');
|
||||
}
|
||||
|
||||
// Check if venue is being used by any events
|
||||
$events_using_venue = get_posts(array(
|
||||
'post_type' => class_exists('Tribe__Events__Main') ? Tribe__Events__Main::POSTTYPE : 'tribe_events',
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => '_EventVenueID',
|
||||
'value' => $venue_id,
|
||||
'compare' => '='
|
||||
)
|
||||
),
|
||||
'posts_per_page' => 1
|
||||
));
|
||||
|
||||
if (!empty($events_using_venue)) {
|
||||
wp_send_json_error('Cannot delete venue. It is being used by one or more events.');
|
||||
}
|
||||
|
||||
$result = wp_trash_post($venue_id);
|
||||
|
||||
if ($result) {
|
||||
wp_send_json_success('Venue deleted successfully.');
|
||||
} else {
|
||||
wp_send_json_error('Failed to delete venue.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for loading venue data
|
||||
*/
|
||||
public function ajax_load_venue() {
|
||||
check_ajax_referer('hvac_venues_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('hvac_trainer')) {
|
||||
wp_send_json_error('Unauthorized');
|
||||
}
|
||||
|
||||
$venue_id = isset($_GET['venue_id']) ? intval($_GET['venue_id']) : 0;
|
||||
|
||||
if (!$venue_id) {
|
||||
wp_send_json_error('Invalid venue ID');
|
||||
}
|
||||
|
||||
$venue = get_post($venue_id);
|
||||
if (!$venue) {
|
||||
wp_send_json_error('Venue not found');
|
||||
}
|
||||
|
||||
$venue_data = array(
|
||||
'id' => $venue_id,
|
||||
'name' => $venue->post_title,
|
||||
'description' => $venue->post_content,
|
||||
'address' => get_post_meta($venue_id, '_VenueAddress', true),
|
||||
'city' => get_post_meta($venue_id, '_VenueCity', true),
|
||||
'state' => get_post_meta($venue_id, '_VenueStateProvince', true),
|
||||
'zip' => get_post_meta($venue_id, '_VenueZip', true),
|
||||
'country' => get_post_meta($venue_id, '_VenueCountry', true),
|
||||
'phone' => get_post_meta($venue_id, '_VenuePhone', true),
|
||||
'website' => get_post_meta($venue_id, '_VenueURL', true)
|
||||
);
|
||||
|
||||
wp_send_json_success($venue_data);
|
||||
}
|
||||
}
|
||||
20
templates/page-trainer-organizer-manage.php
Normal file
20
templates/page-trainer-organizer-manage.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* Template Name: Trainer Organizer Manage
|
||||
* Description: Template for creating and editing training organizers
|
||||
*/
|
||||
|
||||
get_header();
|
||||
?>
|
||||
|
||||
<div class="hvac-page-wrapper hvac-trainer-organizer-manage-page">
|
||||
<div class="container">
|
||||
<?php
|
||||
// Render the organizer manage shortcode
|
||||
echo do_shortcode('[hvac_trainer_organizer_manage]');
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
get_footer();
|
||||
20
templates/page-trainer-organizers-list.php
Normal file
20
templates/page-trainer-organizers-list.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* Template Name: Trainer Organizers List
|
||||
* Description: Template for listing all training organizers
|
||||
*/
|
||||
|
||||
get_header();
|
||||
?>
|
||||
|
||||
<div class="hvac-page-wrapper hvac-trainer-organizers-list-page">
|
||||
<div class="container">
|
||||
<?php
|
||||
// Render the organizers list shortcode
|
||||
echo do_shortcode('[hvac_trainer_organizers_list]');
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
get_footer();
|
||||
20
templates/page-trainer-profile-edit.php
Normal file
20
templates/page-trainer-profile-edit.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* Template Name: Trainer Profile Edit
|
||||
* Description: Template for editing trainer profile
|
||||
*/
|
||||
|
||||
get_header();
|
||||
?>
|
||||
|
||||
<div class="hvac-page-wrapper hvac-trainer-profile-edit-page">
|
||||
<div class="container">
|
||||
<?php
|
||||
// Render the profile edit shortcode
|
||||
echo do_shortcode('[hvac_trainer_profile_edit]');
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
get_footer();
|
||||
20
templates/page-trainer-profile-view.php
Normal file
20
templates/page-trainer-profile-view.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* Template Name: Trainer Profile View
|
||||
* Description: Template for viewing trainer profile
|
||||
*/
|
||||
|
||||
get_header();
|
||||
?>
|
||||
|
||||
<div class="hvac-page-wrapper hvac-trainer-profile-page">
|
||||
<div class="container">
|
||||
<?php
|
||||
// Render the profile view shortcode
|
||||
echo do_shortcode('[hvac_trainer_profile_view]');
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
get_footer();
|
||||
20
templates/page-trainer-venue-manage.php
Normal file
20
templates/page-trainer-venue-manage.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* Template Name: Trainer Venue Manage
|
||||
* Description: Template for creating and editing training venues
|
||||
*/
|
||||
|
||||
get_header();
|
||||
?>
|
||||
|
||||
<div class="hvac-page-wrapper hvac-trainer-venue-manage-page">
|
||||
<div class="container">
|
||||
<?php
|
||||
// Render the venue manage shortcode
|
||||
echo do_shortcode('[hvac_trainer_venue_manage]');
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
get_footer();
|
||||
20
templates/page-trainer-venues-list.php
Normal file
20
templates/page-trainer-venues-list.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* Template Name: Trainer Venues List
|
||||
* Description: Template for listing all training venues
|
||||
*/
|
||||
|
||||
get_header();
|
||||
?>
|
||||
|
||||
<div class="hvac-page-wrapper hvac-trainer-venues-list-page">
|
||||
<div class="container">
|
||||
<?php
|
||||
// Render the venues list shortcode
|
||||
echo do_shortcode('[hvac_trainer_venues_list]');
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
get_footer();
|
||||
Loading…
Reference in a new issue