| |
| <?php
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| if ( ! defined( 'ABSPATH' ) ) {
|
| exit;
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| function bha_compliance_get_user_group_id() {
|
|
|
| if ( ! is_user_logged_in() ) {
|
| return false;
|
| }
|
|
|
| $user_id = get_current_user_id();
|
|
|
|
|
| $group_id = get_user_meta( $user_id, 'group_id', true );
|
| if ( $group_id ) {
|
| return absint( $group_id );
|
| }
|
|
|
|
|
| if ( function_exists( 'groups_get_user_groups' ) ) {
|
| $user_groups = groups_get_user_groups( $user_id );
|
| if ( ! empty( $user_groups['groups'] ) ) {
|
| return absint( $user_groups['groups'][0] );
|
| }
|
| }
|
|
|
| return false;
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| function bha_compliance_get_tracker( $group_id, $year = null ) {
|
|
|
| $group_id = absint( $group_id );
|
| if ( $group_id < 1 ) {
|
| return null;
|
| }
|
|
|
| if ( ! $year ) {
|
| $year = (int) wp_date( 'Y' );
|
| } else {
|
| $year = absint( $year );
|
| if ( $year < 2020 || $year > 2050 ) {
|
| $year = (int) wp_date( 'Y' );
|
| }
|
| }
|
|
|
| $trackers = get_posts( array(
|
| 'post_type' => 'compliance_tracker',
|
| 'post_status' => 'publish',
|
| 'posts_per_page' => 1,
|
| 'meta_query' => array(
|
| 'relation' => 'AND',
|
| array(
|
| 'key' => 'group_id',
|
| 'value' => $group_id,
|
| 'compare' => '=',
|
| 'type' => 'NUMERIC',
|
| ),
|
| array(
|
| 'key' => 'tracking_year',
|
| 'value' => $year,
|
| 'compare' => '=',
|
| 'type' => 'NUMERIC',
|
| ),
|
| ),
|
| ) );
|
|
|
| return ! empty( $trackers ) ? $trackers[0] : null;
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| function bha_compliance_get_data( $post_id ) {
|
|
|
| $post_id = absint( $post_id );
|
| if ( $post_id < 1 ) {
|
| return array( 'error' => true, 'message' => 'Invalid post ID' );
|
| }
|
|
|
| $post = get_post( $post_id );
|
| if ( ! $post || $post->post_type !== 'compliance_tracker' ) {
|
| return array( 'error' => true, 'message' => 'Invalid compliance tracker' );
|
| }
|
|
|
| $data = array(
|
| 'error' => false,
|
| 'organization_name' => sanitize_text_field( get_field( 'organization_name', $post_id ) ) ?: 'Unknown',
|
| 'tracking_year' => absint( get_field( 'tracking_year', $post_id ) ) ?: (int) wp_date( 'Y' ),
|
| 'monthly_due_day' => absint( get_field( 'monthly_due_day', $post_id ) ) ?: 15,
|
| 'monthly_items' => array(),
|
| 'quarterly_items' => array(),
|
| 'annual_items' => array(),
|
| 'totals' => array(
|
| 'total' => 0,
|
| 'completed' => 0,
|
| 'pending' => 0,
|
| 'overdue' => 0,
|
| ),
|
| );
|
|
|
| $today = wp_date( 'Y-m-d' );
|
| $tracking_year = $data['tracking_year'];
|
| $monthly_due_day = min( max( $data['monthly_due_day'], 1 ), 28 );
|
|
|
|
|
| $monthly_config = array(
|
| 'emergency_drill' => 'Emergency Drill',
|
| 'budget_projected' => 'Budget Projected',
|
| 'budget_actual' => 'Budget Actual',
|
| 'vehicle_inspection' => 'Vehicle Inspection',
|
| 'transportation_mileage' => 'Transportation Mileage',
|
| 'staff_meeting' => 'Staff Meeting',
|
| 'hs_inspection' => 'H&S Inspection',
|
| 'clinical_supervision' => 'Clinical Supervision',
|
| 'grievances' => 'Grievances',
|
| 'incident_reports' => 'Incident Reports',
|
| 'client_accommodation' => 'Client Accommodation',
|
| 'staff_accommodation' => 'Staff Accommodation',
|
| );
|
|
|
| $months = array(
|
| 1 => 'jan', 2 => 'feb', 3 => 'mar', 4 => 'apr',
|
| 5 => 'may', 6 => 'jun', 7 => 'jul', 8 => 'aug',
|
| 9 => 'sep', 10 => 'oct', 11 => 'nov', 12 => 'dec',
|
| );
|
|
|
| $month_names = array(
|
| 1 => 'January', 2 => 'February', 3 => 'March', 4 => 'April',
|
| 5 => 'May', 6 => 'June', 7 => 'July', 8 => 'August',
|
| 9 => 'September', 10 => 'October', 11 => 'November', 12 => 'December',
|
| );
|
|
|
|
|
| foreach ( $monthly_config as $item_key => $item_label ) {
|
| $item_data = array(
|
| 'key' => $item_key,
|
| 'label' => $item_label,
|
| 'months' => array(),
|
| );
|
|
|
| foreach ( $months as $month_num => $month_abbr ) {
|
| $due_date = sprintf( '%d-%02d-%02d', $tracking_year, $month_num, $monthly_due_day );
|
| $field_key_complete = $item_key . '_' . $month_abbr . '_complete';
|
| $field_key_date = $item_key . '_' . $month_abbr . '_date';
|
|
|
| $is_complete = (bool) get_field( $field_key_complete, $post_id );
|
| $completed_date = get_field( $field_key_date, $post_id );
|
|
|
| if ( $completed_date ) {
|
| $completed_date = sanitize_text_field( $completed_date );
|
| }
|
|
|
| $status = 'future';
|
| if ( $is_complete ) {
|
| $status = 'complete';
|
| $data['totals']['completed']++;
|
| } elseif ( $due_date <= $today ) {
|
| $status = 'overdue';
|
| $data['totals']['overdue']++;
|
| } else {
|
| $status = 'pending';
|
| $data['totals']['pending']++;
|
| }
|
|
|
| $data['totals']['total']++;
|
|
|
| $item_data['months'][ $month_abbr ] = array(
|
| 'month_num' => $month_num,
|
| 'month_name' => $month_names[ $month_num ],
|
| 'due_date' => $due_date,
|
| 'is_complete' => $is_complete,
|
| 'completed_date' => $completed_date,
|
| 'status' => $status,
|
| );
|
| }
|
|
|
| $data['monthly_items'][ $item_key ] = $item_data;
|
| }
|
|
|
|
|
| $quarterly_config = array(
|
| 'billing_audit' => 'Billing Audit',
|
| 'clinical_record_review' => 'Clinical Record Review',
|
| );
|
|
|
| $quarter_due_dates = array(
|
| 1 => sanitize_text_field( get_field( 'q1_due_date', $post_id ) ) ?: sprintf( '%d-03-31', $tracking_year ),
|
| 2 => sanitize_text_field( get_field( 'q2_due_date', $post_id ) ) ?: sprintf( '%d-06-30', $tracking_year ),
|
| 3 => sanitize_text_field( get_field( 'q3_due_date', $post_id ) ) ?: sprintf( '%d-09-30', $tracking_year ),
|
| 4 => sanitize_text_field( get_field( 'q4_due_date', $post_id ) ) ?: sprintf( '%d-12-31', $tracking_year ),
|
| );
|
|
|
| foreach ( $quarterly_config as $item_key => $item_label ) {
|
| $item_data = array(
|
| 'key' => $item_key,
|
| 'label' => $item_label,
|
| 'quarters' => array(),
|
| );
|
|
|
| for ( $q = 1; $q <= 4; $q++ ) {
|
| $due_date = $quarter_due_dates[ $q ];
|
| $field_key_complete = $item_key . '_q' . $q . '_complete';
|
| $field_key_date = $item_key . '_q' . $q . '_date';
|
|
|
| $is_complete = (bool) get_field( $field_key_complete, $post_id );
|
| $completed_date = get_field( $field_key_date, $post_id );
|
|
|
| if ( $completed_date ) {
|
| $completed_date = sanitize_text_field( $completed_date );
|
| }
|
|
|
| $status = 'future';
|
| if ( $is_complete ) {
|
| $status = 'complete';
|
| $data['totals']['completed']++;
|
| } elseif ( $due_date <= $today ) {
|
| $status = 'overdue';
|
| $data['totals']['overdue']++;
|
| } else {
|
| $status = 'pending';
|
| $data['totals']['pending']++;
|
| }
|
|
|
| $data['totals']['total']++;
|
|
|
| $item_data['quarters'][ $q ] = array(
|
| 'quarter' => 'Q' . $q,
|
| 'due_date' => $due_date,
|
| 'is_complete' => $is_complete,
|
| 'completed_date' => $completed_date,
|
| 'status' => $status,
|
| );
|
| }
|
|
|
| $data['quarterly_items'][ $item_key ] = $item_data;
|
| }
|
|
|
|
|
| $annual_config = array(
|
| 'strategic_meeting' => 'Strategic Meeting',
|
| 'satisfaction_surveys' => 'Satisfaction Surveys',
|
| );
|
|
|
| $annual_due_date = sanitize_text_field( get_field( 'annual_due_date', $post_id ) ) ?: sprintf( '%d-12-15', $tracking_year );
|
|
|
| foreach ( $annual_config as $item_key => $item_label ) {
|
| $field_key_complete = $item_key . '_complete';
|
| $field_key_date = $item_key . '_date';
|
|
|
| $is_complete = (bool) get_field( $field_key_complete, $post_id );
|
| $completed_date = get_field( $field_key_date, $post_id );
|
|
|
| if ( $completed_date ) {
|
| $completed_date = sanitize_text_field( $completed_date );
|
| }
|
|
|
| $status = 'future';
|
| if ( $is_complete ) {
|
| $status = 'complete';
|
| $data['totals']['completed']++;
|
| } elseif ( $annual_due_date <= $today ) {
|
| $status = 'overdue';
|
| $data['totals']['overdue']++;
|
| } else {
|
| $status = 'pending';
|
| $data['totals']['pending']++;
|
| }
|
|
|
| $data['totals']['total']++;
|
|
|
| $data['annual_items'][ $item_key ] = array(
|
| 'key' => $item_key,
|
| 'label' => $item_label,
|
| 'due_date' => $annual_due_date,
|
| 'is_complete' => $is_complete,
|
| 'completed_date' => $completed_date,
|
| 'status' => $status,
|
| );
|
| }
|
|
|
|
|
| $data['totals']['percentage'] = $data['totals']['total'] > 0
|
| ? round( ( $data['totals']['completed'] / $data['totals']['total'] ) * 100, 1 )
|
| : 0;
|
|
|
| return $data;
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| function bha_compliance_get_month_data( $post_id, $month, $month_num ) {
|
|
|
| $post_id = absint( $post_id );
|
| $tracking_year = absint( get_field( 'tracking_year', $post_id ) ) ?: (int) wp_date( 'Y' );
|
| $monthly_due_day = absint( get_field( 'monthly_due_day', $post_id ) ) ?: 15;
|
|
|
| $today = wp_date( 'Y-m-d' );
|
| $due_date = sprintf( '%d-%02d-%02d', $tracking_year, $month_num, $monthly_due_day );
|
|
|
| $monthly_items = array(
|
| 'emergency_drill' => 'Emergency Drill',
|
| 'budget_projected' => 'Budget Projected',
|
| 'budget_actual' => 'Budget Actual',
|
| 'vehicle_inspection' => 'Vehicle Inspection',
|
| 'transportation_mileage' => 'Transportation Mileage',
|
| 'staff_meeting' => 'Staff Meeting',
|
| 'hs_inspection' => 'H&S Inspection',
|
| 'clinical_supervision' => 'Clinical Supervision',
|
| 'grievances' => 'Grievances',
|
| 'incident_reports' => 'Incident Reports',
|
| 'client_accommodation' => 'Client Accommodation',
|
| 'staff_accommodation' => 'Staff Accommodation',
|
| );
|
|
|
| $total = 0;
|
| $completed = 0;
|
| $overdue = 0;
|
| $items = array();
|
|
|
| foreach ( $monthly_items as $item_key => $item_label ) {
|
| $field_complete = $item_key . '_' . $month . '_complete';
|
| $field_date = $item_key . '_' . $month . '_date';
|
| $field_entry = $item_key . '_' . $month . '_entry_id';
|
|
|
| $is_complete = (bool) get_field( $field_complete, $post_id );
|
| $completed_date = get_field( $field_date, $post_id );
|
| $entry_id = get_field( $field_entry, $post_id );
|
|
|
| $total++;
|
|
|
| $status = 'future';
|
| if ( $is_complete ) {
|
| $status = 'complete';
|
| $completed++;
|
| } elseif ( $due_date < $today ) {
|
| $status = 'overdue';
|
| $overdue++;
|
| } elseif ( $due_date <= $today ) {
|
| $status = 'pending';
|
| }
|
|
|
| $items[] = array(
|
| 'key' => $item_key,
|
| 'label' => $item_label,
|
| 'status' => $status,
|
| 'is_complete' => $is_complete,
|
| 'completed_date' => $completed_date,
|
| 'entry_id' => $entry_id,
|
| 'frequency' => 'monthly',
|
| );
|
| }
|
|
|
|
|
| $is_quarterly_month = in_array( $month_num, array( 3, 6, 9, 12 ), true );
|
| $quarter = (int) ceil( $month_num / 3 );
|
|
|
| if ( $is_quarterly_month ) {
|
| $quarterly_items = array(
|
| 'billing_audit' => 'Billing Audit',
|
| 'clinical_record_review' => 'Clinical Record Review',
|
| );
|
|
|
| $quarter_due_dates = array(
|
| 1 => sprintf( '%d-03-31', $tracking_year ),
|
| 2 => sprintf( '%d-06-30', $tracking_year ),
|
| 3 => sprintf( '%d-09-30', $tracking_year ),
|
| 4 => sprintf( '%d-12-31', $tracking_year ),
|
| );
|
|
|
| $q_due = $quarter_due_dates[ $quarter ];
|
|
|
| foreach ( $quarterly_items as $item_key => $item_label ) {
|
| $field_complete = $item_key . '_q' . $quarter . '_complete';
|
| $field_date = $item_key . '_q' . $quarter . '_date';
|
| $field_entry = $item_key . '_q' . $quarter . '_entry_id';
|
|
|
| $is_complete = (bool) get_field( $field_complete, $post_id );
|
| $completed_date = get_field( $field_date, $post_id );
|
| $entry_id = get_field( $field_entry, $post_id );
|
|
|
| $total++;
|
|
|
| $status = 'future';
|
| if ( $is_complete ) {
|
| $status = 'complete';
|
| $completed++;
|
| } elseif ( $q_due < $today ) {
|
| $status = 'overdue';
|
| $overdue++;
|
| } elseif ( $q_due <= $today ) {
|
| $status = 'pending';
|
| }
|
|
|
| $items[] = array(
|
| 'key' => $item_key,
|
| 'label' => $item_label . ' (Q' . $quarter . ')',
|
| 'status' => $status,
|
| 'is_complete' => $is_complete,
|
| 'completed_date' => $completed_date,
|
| 'entry_id' => $entry_id,
|
| 'frequency' => 'quarterly',
|
| 'quarter' => $quarter,
|
| );
|
| }
|
| }
|
|
|
|
|
| $is_annual_month = ( $month_num === 12 );
|
|
|
| if ( $is_annual_month ) {
|
| $annual_items = array(
|
| 'strategic_meeting' => 'Strategic Meeting',
|
| 'satisfaction_surveys' => 'Satisfaction Surveys',
|
| );
|
|
|
| $annual_due = sprintf( '%d-12-15', $tracking_year );
|
|
|
| foreach ( $annual_items as $item_key => $item_label ) {
|
| $field_complete = $item_key . '_complete';
|
| $field_date = $item_key . '_date';
|
| $field_entry = $item_key . '_entry_id';
|
|
|
| $is_complete = (bool) get_field( $field_complete, $post_id );
|
| $completed_date = get_field( $field_date, $post_id );
|
| $entry_id = get_field( $field_entry, $post_id );
|
|
|
| $total++;
|
|
|
| $status = 'future';
|
| if ( $is_complete ) {
|
| $status = 'complete';
|
| $completed++;
|
| } elseif ( $annual_due < $today ) {
|
| $status = 'overdue';
|
| $overdue++;
|
| } elseif ( $annual_due <= $today ) {
|
| $status = 'pending';
|
| }
|
|
|
| $items[] = array(
|
| 'key' => $item_key,
|
| 'label' => $item_label . ' (Annual)',
|
| 'status' => $status,
|
| 'is_complete' => $is_complete,
|
| 'completed_date' => $completed_date,
|
| 'entry_id' => $entry_id,
|
| 'frequency' => 'annual',
|
| );
|
| }
|
| }
|
|
|
| $percentage = $total > 0 ? round( ( $completed / $total ) * 100 ) : 0;
|
|
|
| return array(
|
| 'month' => $month,
|
| 'month_num' => $month_num,
|
| 'total' => $total,
|
| 'completed' => $completed,
|
| 'overdue' => $overdue,
|
| 'pending' => $total - $completed - $overdue,
|
| 'percentage' => $percentage,
|
| 'due_date' => $due_date,
|
| 'is_quarterly_month' => $is_quarterly_month,
|
| 'is_annual_month' => $is_annual_month,
|
| 'quarter' => $is_quarterly_month ? $quarter : null,
|
| 'items' => $items,
|
| );
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| function bha_compliance_render_progress_bar( $totals ) {
|
|
|
| $percentage = isset( $totals['percentage'] ) ? floatval( $totals['percentage'] ) : 0;
|
| $completed = isset( $totals['completed'] ) ? absint( $totals['completed'] ) : 0;
|
| $total = isset( $totals['total'] ) ? absint( $totals['total'] ) : 0;
|
|
|
| $percentage = max( 0, min( 100, $percentage ) );
|
|
|
| $bar_class = 'progress-critical';
|
| if ( $percentage >= 80 ) {
|
| $bar_class = 'progress-good';
|
| } elseif ( $percentage >= 50 ) {
|
| $bar_class = 'progress-warning';
|
| }
|
|
|
| ob_start();
|
| ?>
|
| <div class="compliance-progress">
|
| <div class="progress-header">
|
| <span class="progress-label">Overall Compliance</span>
|
| <span class="progress-percentage"><?php echo esc_html( $percentage ); ?>%</span>
|
| </div>
|
| <div class="progress-bar-container">
|
| <div class="progress-bar <?php echo esc_attr( $bar_class ); ?>" style="width: <?php echo esc_attr( $percentage ); ?>%;"></div>
|
| </div>
|
| <div class="progress-footer">
|
| <?php echo esc_html( $completed ); ?> of <?php echo esc_html( $total ); ?> items completed
|
| </div>
|
| </div>
|
| <?php
|
| return ob_get_clean();
|
| }
|
|
|
| /**
|
| * =============================================================================
|
| * SHORTCODE 1: Full Dashboard (Table View)
|
| * [compliance_dashboard group_id="123" year="2025"]
|
| * =============================================================================
|
| */
|
| function bha_compliance_dashboard_shortcode( $atts ) {
|
|
|
| $atts = shortcode_atts( array(
|
| 'group_id' => '',
|
| 'year' => '',
|
| ), $atts, 'compliance_dashboard' );
|
|
|
|
|
| $group_id = ! empty( $atts['group_id'] ) ? absint( $atts['group_id'] ) : bha_compliance_get_user_group_id();
|
|
|
| if ( ! $group_id ) {
|
| return '<div class="compliance-error" style="padding:15px;background:#f8d7da;border:1px solid #f5c6cb;border-radius:4px;color:#721c24;">No organization found. Please contact support.</div>';
|
| }
|
|
|
|
|
| $year = ! empty( $atts['year'] ) ? absint( $atts['year'] ) : (int) wp_date( 'Y' );
|
| if ( $year < 2020 || $year > 2050 ) {
|
| $year = (int) wp_date( 'Y' );
|
| }
|
|
|
|
|
| $tracker = bha_compliance_get_tracker( $group_id, $year );
|
|
|
| if ( ! $tracker ) {
|
| return '<div class="compliance-error" style="padding:15px;background:#f8d7da;border:1px solid #f5c6cb;border-radius:4px;color:#721c24;">No compliance tracker found for this organization and year.</div>';
|
| }
|
|
|
|
|
| $data = bha_compliance_get_data( $tracker->ID );
|
|
|
| if ( isset( $data['error'] ) && $data['error'] ) {
|
| return '<div class="compliance-error" style="padding:15px;background:#f8d7da;border:1px solid #f5c6cb;border-radius:4px;color:#721c24;">' . esc_html( $data['message'] ) . '</div>';
|
| }
|
|
|
| ob_start();
|
| ?>
|
| <div class="compliance-dashboard" data-group-id="<?php echo esc_attr( $group_id ); ?>" data-year="<?php echo esc_attr( $year ); ?>">
|
|
|
| <div class="compliance-header">
|
| <h2 style="margin:0 0 20px 0;font-size:24px;color:#333;"><?php echo esc_html( $data['organization_name'] ); ?> - <?php echo esc_html( $data['tracking_year'] ); ?> Compliance</h2>
|
| </div>
|
|
|
| <?php echo bha_compliance_render_progress_bar( $data['totals'] ); ?>
|
|
|
| <div class="compliance-stats" style="display:grid;grid-template-columns:repeat(4,1fr);gap:15px;margin-bottom:30px;">
|
| <div class="stat-box" style="padding:20px;border-radius:8px;text-align:center;color:#fff;background:#6c757d;">
|
| <span class="stat-number" style="display:block;font-size:32px;font-weight:700;"><?php echo esc_html( $data['totals']['total'] ); ?></span>
|
| <span class="stat-label" style="display:block;font-size:14px;opacity:0.9;">Total Items</span>
|
| </div>
|
| <div class="stat-box" style="padding:20px;border-radius:8px;text-align:center;color:#fff;background:#28a745;">
|
| <span class="stat-number" style="display:block;font-size:32px;font-weight:700;"><?php echo esc_html( $data['totals']['completed'] ); ?></span>
|
| <span class="stat-label" style="display:block;font-size:14px;opacity:0.9;">Completed</span>
|
| </div>
|
| <div class="stat-box" style="padding:20px;border-radius:8px;text-align:center;color:#333;background:#ffc107;">
|
| <span class="stat-number" style="display:block;font-size:32px;font-weight:700;"><?php echo esc_html( $data['totals']['pending'] ); ?></span>
|
| <span class="stat-label" style="display:block;font-size:14px;opacity:0.9;">Pending</span>
|
| </div>
|
| <div class="stat-box" style="padding:20px;border-radius:8px;text-align:center;color:#fff;background:#dc3545;">
|
| <span class="stat-number" style="display:block;font-size:32px;font-weight:700;"><?php echo esc_html( $data['totals']['overdue'] ); ?></span>
|
| <span class="stat-label" style="display:block;font-size:14px;opacity:0.9;">Overdue</span>
|
| </div>
|
| </div>
|
|
|
| <div class="compliance-section" style="margin-bottom:30px;">
|
| <h3 style="margin:0 0 15px 0;font-size:18px;color:#333;border-bottom:2px solid #007bff;padding-bottom:8px;">Monthly Compliance Items</h3>
|
| <div style="overflow-x:auto;">
|
| <table class="compliance-table" style="width:100%;border-collapse:collapse;font-size:14px;">
|
| <thead>
|
| <tr>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:left;">Item</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Jan</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Feb</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Mar</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Apr</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">May</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Jun</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Jul</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Aug</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Sep</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Oct</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Nov</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Dec</th>
|
| </tr>
|
| </thead>
|
| <tbody>
|
| <?php foreach ( $data['monthly_items'] as $item ) : ?>
|
| <tr>
|
| <td style="padding:10px 8px;border:1px solid #dee2e6;text-align:left;font-weight:500;white-space:nowrap;"><?php echo esc_html( $item['label'] ); ?></td>
|
| <?php foreach ( $item['months'] as $month ) :
|
| $bg_color = '#f8f9fa';
|
| $text_color = '#6c757d';
|
| if ( $month['status'] === 'complete' ) {
|
| $bg_color = '#d4edda';
|
| $text_color = '#155724';
|
| } elseif ( $month['status'] === 'overdue' ) {
|
| $bg_color = '#f8d7da';
|
| $text_color = '#721c24';
|
| } elseif ( $month['status'] === 'pending' ) {
|
| $bg_color = '#fff3cd';
|
| $text_color = '#856404';
|
| }
|
| ?>
|
| <td style="padding:10px 8px;border:1px solid #dee2e6;text-align:center;font-weight:600;background:<?php echo esc_attr( $bg_color ); ?>;color:<?php echo esc_attr( $text_color ); ?>;" title="<?php echo esc_attr( $month['month_name'] . ' - ' . ucfirst( $month['status'] ) ); ?>">
|
| <?php echo $month['is_complete'] ? '✓' : '–'; ?>
|
| </td>
|
| <?php endforeach; ?>
|
| </tr>
|
| <?php endforeach; ?>
|
| </tbody>
|
| </table>
|
| </div>
|
| </div>
|
|
|
| <div class="compliance-section" style="margin-bottom:30px;">
|
| <h3 style="margin:0 0 15px 0;font-size:18px;color:#333;border-bottom:2px solid #007bff;padding-bottom:8px;">Quarterly Compliance Items</h3>
|
| <div style="overflow-x:auto;">
|
| <table class="compliance-table" style="width:100%;border-collapse:collapse;font-size:14px;">
|
| <thead>
|
| <tr>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:left;">Item</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Q1 (Mar 31)</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Q2 (Jun 30)</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Q3 (Sep 30)</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Q4 (Dec 31)</th>
|
| </tr>
|
| </thead>
|
| <tbody>
|
| <?php foreach ( $data['quarterly_items'] as $item ) : ?>
|
| <tr>
|
| <td style="padding:10px 8px;border:1px solid #dee2e6;text-align:left;font-weight:500;white-space:nowrap;"><?php echo esc_html( $item['label'] ); ?></td>
|
| <?php foreach ( $item['quarters'] as $quarter ) :
|
| $bg_color = '#f8f9fa';
|
| $text_color = '#6c757d';
|
| if ( $quarter['status'] === 'complete' ) {
|
| $bg_color = '#d4edda';
|
| $text_color = '#155724';
|
| } elseif ( $quarter['status'] === 'overdue' ) {
|
| $bg_color = '#f8d7da';
|
| $text_color = '#721c24';
|
| } elseif ( $quarter['status'] === 'pending' ) {
|
| $bg_color = '#fff3cd';
|
| $text_color = '#856404';
|
| }
|
| ?>
|
| <td style="padding:10px 8px;border:1px solid #dee2e6;text-align:center;font-weight:600;background:<?php echo esc_attr( $bg_color ); ?>;color:<?php echo esc_attr( $text_color ); ?>;" title="<?php echo esc_attr( $quarter['quarter'] . ' - ' . ucfirst( $quarter['status'] ) ); ?>">
|
| <?php echo $quarter['is_complete'] ? '✓' : '–'; ?>
|
| </td>
|
| <?php endforeach; ?>
|
| </tr>
|
| <?php endforeach; ?>
|
| </tbody>
|
| </table>
|
| </div>
|
| </div>
|
|
|
| <div class="compliance-section" style="margin-bottom:30px;">
|
| <h3 style="margin:0 0 15px 0;font-size:18px;color:#333;border-bottom:2px solid #007bff;padding-bottom:8px;">Annual Compliance Items</h3>
|
| <div style="overflow-x:auto;">
|
| <table class="compliance-table" style="width:100%;border-collapse:collapse;font-size:14px;">
|
| <thead>
|
| <tr>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:left;">Item</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Due Date</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Status</th>
|
| <th style="padding:10px 8px;border:1px solid #dee2e6;background:#f8f9fa;font-weight:600;text-align:center;">Completed</th>
|
| </tr>
|
| </thead>
|
| <tbody>
|
| <?php foreach ( $data['annual_items'] as $item ) :
|
| $bg_color = '#f8f9fa';
|
| $text_color = '#6c757d';
|
| if ( $item['status'] === 'complete' ) {
|
| $bg_color = '#d4edda';
|
| $text_color = '#155724';
|
| } elseif ( $item['status'] === 'overdue' ) {
|
| $bg_color = '#f8d7da';
|
| $text_color = '#721c24';
|
| } elseif ( $item['status'] === 'pending' ) {
|
| $bg_color = '#fff3cd';
|
| $text_color = '#856404';
|
| }
|
| ?>
|
| <tr>
|
| <td style="padding:10px 8px;border:1px solid #dee2e6;text-align:left;font-weight:500;"><?php echo esc_html( $item['label'] ); ?></td>
|
| <td style="padding:10px 8px;border:1px solid #dee2e6;text-align:center;"><?php echo esc_html( date( 'M j, Y', strtotime( $item['due_date'] ) ) ); ?></td>
|
| <td style="padding:10px 8px;border:1px solid #dee2e6;text-align:center;font-weight:600;background:<?php echo esc_attr( $bg_color ); ?>;color:<?php echo esc_attr( $text_color ); ?>;">
|
| <?php echo esc_html( ucfirst( $item['status'] ) ); ?>
|
| </td>
|
| <td style="padding:10px 8px;border:1px solid #dee2e6;text-align:center;">
|
| <?php echo $item['completed_date'] ? esc_html( date( 'M j, Y', strtotime( $item['completed_date'] ) ) ) : '–'; ?>
|
| </td>
|
| </tr>
|
| <?php endforeach; ?>
|
| </tbody>
|
| </table>
|
| </div>
|
| </div>
|
|
|
| </div>
|
|
|
| <style>
|
| .compliance-dashboard { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; max-width: 100%; margin: 0 auto; }
|
| .compliance-progress { margin-bottom: 25px; }
|
| .progress-header { display: flex; justify-content: space-between; margin-bottom: 8px; }
|
| .progress-label { font-weight: 600; color:
|
| .progress-percentage { font-weight: 700; font-size: 18px; }
|
| .progress-bar-container { height: 24px; background:
|
| .progress-bar { height: 100%; border-radius: 12px; transition: width 0.5s ease; }
|
| .progress-good { background: linear-gradient(90deg,
|
| .progress-warning { background: linear-gradient(90deg,
|
| .progress-critical { background: linear-gradient(90deg,
|
| .progress-footer { margin-top: 8px; font-size: 14px; color:
|
| @media (max-width: 768px) {
|
| .compliance-stats { grid-template-columns: repeat(2, 1fr) !important; }
|
| .stat-number { font-size: 24px !important; }
|
| .compliance-table { font-size: 12px !important; }
|
| }
|
| @media (max-width: 480px) {
|
| .compliance-stats { grid-template-columns: 1fr !important; }
|
| }
|
| </style>
|
| <?php
|
| return ob_get_clean();
|
| }
|
| add_shortcode( 'compliance_dashboard', 'bha_compliance_dashboard_shortcode' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| function bha_compliance_month_cards_shortcode( $atts ) {
|
|
|
| $atts = shortcode_atts( array(
|
| 'group_id' => '',
|
| 'year' => '',
|
| 'detail_page' => '',
|
| ), $atts, 'compliance_month_cards' );
|
|
|
| $group_id = ! empty( $atts['group_id'] ) ? absint( $atts['group_id'] ) : bha_compliance_get_user_group_id();
|
|
|
| if ( ! $group_id ) {
|
| return '<div class="cc-error">Please log in to view your compliance dashboard.</div>';
|
| }
|
|
|
| $year = ! empty( $atts['year'] ) ? absint( $atts['year'] ) : (int) wp_date( 'Y' );
|
| if ( $year < 2020 || $year > 2050 ) {
|
| $year = (int) wp_date( 'Y' );
|
| }
|
|
|
| $tracker = bha_compliance_get_tracker( $group_id, $year );
|
|
|
| if ( ! $tracker ) {
|
| return '<div class="cc-error">No compliance tracker found for your organization. Please contact support.</div>';
|
| }
|
|
|
| $org_name = sanitize_text_field( get_field( 'organization_name', $tracker->ID ) ) ?: 'Your Organization';
|
| $detail_page = ! empty( $atts['detail_page'] ) ? absint( $atts['detail_page'] ) : 0;
|
|
|
| $months = array(
|
| 1 => array( 'abbr' => 'jan', 'name' => 'January', 'short' => 'JAN' ),
|
| 2 => array( 'abbr' => 'feb', 'name' => 'February', 'short' => 'FEB' ),
|
| 3 => array( 'abbr' => 'mar', 'name' => 'March', 'short' => 'MAR' ),
|
| 4 => array( 'abbr' => 'apr', 'name' => 'April', 'short' => 'APR' ),
|
| 5 => array( 'abbr' => 'may', 'name' => 'May', 'short' => 'MAY' ),
|
| 6 => array( 'abbr' => 'jun', 'name' => 'June', 'short' => 'JUN' ),
|
| 7 => array( 'abbr' => 'jul', 'name' => 'July', 'short' => 'JUL' ),
|
| 8 => array( 'abbr' => 'aug', 'name' => 'August', 'short' => 'AUG' ),
|
| 9 => array( 'abbr' => 'sep', 'name' => 'September', 'short' => 'SEP' ),
|
| 10 => array( 'abbr' => 'oct', 'name' => 'October', 'short' => 'OCT' ),
|
| 11 => array( 'abbr' => 'nov', 'name' => 'November', 'short' => 'NOV' ),
|
| 12 => array( 'abbr' => 'dec', 'name' => 'December', 'short' => 'DEC' ),
|
| );
|
|
|
| $current_month = (int) wp_date( 'n' );
|
|
|
|
|
| $overall_total = 0;
|
| $overall_completed = 0;
|
| foreach ( $months as $m_num => $m_info ) {
|
| $m_data = bha_compliance_get_month_data( $tracker->ID, $m_info['abbr'], $m_num );
|
| $overall_total += $m_data['total'];
|
| $overall_completed += $m_data['completed'];
|
| }
|
| $overall_pct = $overall_total > 0 ? round( ( $overall_completed / $overall_total ) * 100 ) : 0;
|
|
|
| ob_start();
|
| ?>
|
| <div class="cc-month-dashboard">
|
|
|
| <div class="cc-dashboard-header">
|
| <div class="cc-header-content">
|
| <h2 class="cc-org-title"><?php echo esc_html( $org_name ); ?></h2>
|
| <p class="cc-year-subtitle"><?php echo esc_html( $year ); ?> CARF Compliance Calendar</p>
|
| </div>
|
| <div class="cc-header-badge">
|
| <div class="cc-overall-progress">
|
| <span class="cc-overall-pct"><?php echo esc_html( $overall_pct ); ?>%</span>
|
| <span class="cc-overall-label">Overall</span>
|
| </div>
|
| </div>
|
| </div>
|
|
|
| <div class="cc-month-grid">
|
| <?php foreach ( $months as $month_num => $month_info ) :
|
| $month_data = bha_compliance_get_month_data( $tracker->ID, $month_info['abbr'], $month_num );
|
| $pct = $month_data['percentage'];
|
|
|
| $status_class = 'future';
|
| if ( $month_num < $current_month ) {
|
| $status_class = $pct >= 80 ? 'good' : ( $pct >= 50 ? 'warning' : 'critical' );
|
| } elseif ( $month_num === $current_month ) {
|
| $status_class = 'current';
|
| }
|
|
|
| $detail_link = '';
|
| if ( $detail_page > 0 ) {
|
| $detail_link = add_query_arg( array(
|
| 'month' => $month_info['abbr'],
|
| 'year' => $year,
|
| ), get_permalink( $detail_page ) );
|
| }
|
| ?>
|
| <div class="cc-month-card cc-status-<?php echo esc_attr( $status_class ); ?><?php echo $month_data['is_quarterly_month'] ? ' cc-quarterly' : ''; ?><?php echo $month_data['is_annual_month'] ? ' cc-annual' : ''; ?>">
|
|
|
| <div class="cc-card-badges">
|
| <?php if ( $month_data['is_quarterly_month'] ) : ?>
|
| <span class="cc-badge cc-badge-quarterly">Q<?php echo esc_html( $month_data['quarter'] ); ?></span>
|
| <?php endif; ?>
|
| <?php if ( $month_data['is_annual_month'] ) : ?>
|
| <span class="cc-badge cc-badge-annual">Annual</span>
|
| <?php endif; ?>
|
| </div>
|
|
|
| <div class="cc-month-name"><?php echo esc_html( $month_info['short'] ); ?></div>
|
|
|
| <div class="cc-progress-ring" data-percentage="<?php echo esc_attr( $pct ); ?>">
|
| <svg viewBox="0 0 100 100">
|
| <circle class="cc-ring-bg" cx="50" cy="50" r="42"/>
|
| <circle class="cc-ring-progress" cx="50" cy="50" r="42"
|
| style="stroke-dasharray: <?php echo esc_attr( ( $pct / 100 ) * 264 ); ?> 264;"/>
|
| </svg>
|
| <div class="cc-ring-text"><?php echo esc_html( $pct ); ?>%</div>
|
| </div>
|
|
|
| <div class="cc-month-stats">
|
| <span class="cc-stat cc-stat-complete" title="Completed">✓ <?php echo esc_html( $month_data['completed'] ); ?></span>
|
| <?php if ( $month_data['overdue'] > 0 ) : ?>
|
| <span class="cc-stat cc-stat-overdue" title="Overdue">⚠ <?php echo esc_html( $month_data['overdue'] ); ?></span>
|
| <?php endif; ?>
|
| <span class="cc-stat cc-stat-total" title="Total Items"><?php echo esc_html( $month_data['total'] ); ?> items</span>
|
| </div>
|
|
|
| <?php if ( $detail_link ) : ?>
|
| <a href="<?php echo esc_url( $detail_link ); ?>" class="cc-view-btn">View Details</a>
|
| <?php endif; ?>
|
|
|
| </div>
|
| <?php endforeach; ?>
|
| </div>
|
|
|
| <div class="cc-legend">
|
| <span class="cc-legend-item"><span class="cc-dot cc-dot-good"></span> On Track (≥80%)</span>
|
| <span class="cc-legend-item"><span class="cc-dot cc-dot-warning"></span> Needs Attention (50-79%)</span>
|
| <span class="cc-legend-item"><span class="cc-dot cc-dot-critical"></span> Critical (<50%)</span>
|
| <span class="cc-legend-item"><span class="cc-dot cc-dot-current"></span> Current Month</span>
|
| </div>
|
|
|
| </div>
|
| <?php
|
| echo bha_compliance_month_cards_css();
|
| return ob_get_clean();
|
| }
|
| add_shortcode( 'compliance_month_cards', 'bha_compliance_month_cards_shortcode' );
|
|
|
| /**
|
| * =============================================================================
|
| * SHORTCODE 3: Month Detail View (NEW v1.3)
|
| * [compliance_month_detail month="jan"]
|
| * [compliance_month_detail month="current" group_id="123" year="2025"]
|
| * =============================================================================
|
| */
|
| function bha_compliance_month_detail_shortcode( $atts ) {
|
|
|
| $atts = shortcode_atts( array(
|
| 'month' => 'current',
|
| 'group_id' => '',
|
| 'year' => '',
|
| 'back_page' => '',
|
| ), $atts, 'compliance_month_detail' );
|
|
|
| $group_id = ! empty( $atts['group_id'] ) ? absint( $atts['group_id'] ) : bha_compliance_get_user_group_id();
|
|
|
| if ( ! $group_id ) {
|
| return '<div class="cc-error">Please log in to view compliance details.</div>';
|
| }
|
|
|
| $year = ! empty( $atts['year'] ) ? absint( $atts['year'] ) : 0;
|
| if ( ! $year && isset( $_GET['year'] ) ) {
|
| $year = absint( $_GET['year'] );
|
| }
|
| if ( ! $year || $year < 2020 || $year > 2050 ) {
|
| $year = (int) wp_date( 'Y' );
|
| }
|
|
|
| $month_input = ! empty( $atts['month'] ) ? sanitize_key( $atts['month'] ) : 'current';
|
| if ( isset( $_GET['month'] ) ) {
|
| $month_input = sanitize_key( $_GET['month'] );
|
| }
|
|
|
| $month_map = array(
|
| 'jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4,
|
| 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8,
|
| 'sep' => 9, 'oct' => 10, 'nov' => 11, 'dec' => 12,
|
| );
|
|
|
| $month_names = array(
|
| 1 => 'January', 2 => 'February', 3 => 'March', 4 => 'April',
|
| 5 => 'May', 6 => 'June', 7 => 'July', 8 => 'August',
|
| 9 => 'September', 10 => 'October', 11 => 'November', 12 => 'December',
|
| );
|
|
|
| if ( $month_input === 'current' ) {
|
| $month_num = (int) wp_date( 'n' );
|
| $month_abbr = array_search( $month_num, $month_map );
|
| } elseif ( isset( $month_map[ $month_input ] ) ) {
|
| $month_abbr = $month_input;
|
| $month_num = $month_map[ $month_input ];
|
| } else {
|
| return '<div class="cc-error">Invalid month specified.</div>';
|
| }
|
|
|
| $tracker = bha_compliance_get_tracker( $group_id, $year );
|
|
|
| if ( ! $tracker ) {
|
| return '<div class="cc-error">No compliance tracker found.</div>';
|
| }
|
|
|
| $org_name = sanitize_text_field( get_field( 'organization_name', $tracker->ID ) ) ?: 'Your Organization';
|
| $month_data = bha_compliance_get_month_data( $tracker->ID, $month_abbr, $month_num );
|
| $back_page = ! empty( $atts['back_page'] ) ? absint( $atts['back_page'] ) : 0;
|
|
|
| ob_start();
|
| ?>
|
| <div class="cc-month-detail">
|
|
|
| <div class="cc-detail-header">
|
| <div class="cc-detail-title">
|
| <h2><?php echo esc_html( $month_names[ $month_num ] ); ?> <?php echo esc_html( $year ); ?></h2>
|
| <p class="cc-org-subtitle"><?php echo esc_html( $org_name ); ?></p>
|
| </div>
|
| <div class="cc-detail-progress">
|
| <div class="cc-progress-circle" data-pct="<?php echo esc_attr( $month_data['percentage'] ); ?>">
|
| <span class="cc-pct-value"><?php echo esc_html( $month_data['percentage'] ); ?>%</span>
|
| <span class="cc-pct-label">Complete</span>
|
| </div>
|
| </div>
|
| </div>
|
|
|
| <div class="cc-stats-bar">
|
| <div class="cc-stat-box cc-stat-completed">
|
| <span class="cc-stat-num"><?php echo esc_html( $month_data['completed'] ); ?></span>
|
| <span class="cc-stat-lbl">Completed</span>
|
| </div>
|
| <div class="cc-stat-box cc-stat-pending">
|
| <span class="cc-stat-num"><?php echo esc_html( $month_data['pending'] ); ?></span>
|
| <span class="cc-stat-lbl">Pending</span>
|
| </div>
|
| <div class="cc-stat-box cc-stat-overdue">
|
| <span class="cc-stat-num"><?php echo esc_html( $month_data['overdue'] ); ?></span>
|
| <span class="cc-stat-lbl">Overdue</span>
|
| </div>
|
| <div class="cc-stat-box cc-stat-total">
|
| <span class="cc-stat-num"><?php echo esc_html( $month_data['total'] ); ?></span>
|
| <span class="cc-stat-lbl">Total</span>
|
| </div>
|
| </div>
|
|
|
| <?php if ( $month_data['is_quarterly_month'] || $month_data['is_annual_month'] ) : ?>
|
| <div class="cc-special-badges">
|
| <?php if ( $month_data['is_quarterly_month'] ) : ?>
|
| <span class="cc-special-badge cc-special-quarterly">
|
| ⭐ Quarterly Items Due (Q<?php echo esc_html( $month_data['quarter'] ); ?>)
|
| </span>
|
| <?php endif; ?>
|
| <?php if ( $month_data['is_annual_month'] ) : ?>
|
| <span class="cc-special-badge cc-special-annual">
|
| ⭐ Annual Items Due
|
| </span>
|
| <?php endif; ?>
|
| </div>
|
| <?php endif; ?>
|
|
|
| <div class="cc-items-grid">
|
| <?php foreach ( $month_data['items'] as $item ) :
|
| $status_class = $item['status'];
|
| $status_icon = '○';
|
| $status_text = 'Pending';
|
|
|
| if ( $item['status'] === 'complete' ) {
|
| $status_icon = '✓';
|
| $status_text = 'Complete';
|
| } elseif ( $item['status'] === 'overdue' ) {
|
| $status_icon = '⚠';
|
| $status_text = 'Overdue';
|
| } elseif ( $item['status'] === 'future' ) {
|
| $status_icon = '○';
|
| $status_text = 'Upcoming';
|
| }
|
| ?>
|
| <div class="cc-item-card cc-item-<?php echo esc_attr( $status_class ); ?>">
|
| <div class="cc-item-status">
|
| <span class="cc-status-icon"><?php echo $status_icon; ?></span>
|
| </div>
|
| <div class="cc-item-content">
|
| <span class="cc-item-label"><?php echo esc_html( $item['label'] ); ?></span>
|
| <?php if ( $item['is_complete'] && $item['completed_date'] ) : ?>
|
| <span class="cc-item-date">Completed: <?php echo esc_html( date( 'M j, Y', strtotime( $item['completed_date'] ) ) ); ?></span>
|
| <?php elseif ( $item['status'] === 'overdue' ) : ?>
|
| <span class="cc-item-date cc-date-overdue">Due date passed</span>
|
| <?php endif; ?>
|
| </div>
|
| <div class="cc-item-actions">
|
| <?php if ( $item['is_complete'] && $item['entry_id'] ) : ?>
|
| <span class="cc-action-badge cc-badge-done">Done</span>
|
| <?php else : ?>
|
| <span class="cc-action-badge cc-badge-pending"><?php echo esc_html( $status_text ); ?></span>
|
| <?php endif; ?>
|
| </div>
|
| </div>
|
| <?php endforeach; ?>
|
| </div>
|
|
|
| <?php if ( $back_page > 0 ) : ?>
|
| <div class="cc-back-section">
|
| <a href="<?php echo esc_url( get_permalink( $back_page ) ); ?>" class="cc-back-btn">← Back to Calendar</a>
|
| </div>
|
| <?php endif; ?>
|
|
|
| </div>
|
| <?php
|
| echo bha_compliance_month_cards_css();
|
| return ob_get_clean();
|
| }
|
| add_shortcode( 'compliance_month_detail', 'bha_compliance_month_detail_shortcode' );
|
|
|
| /**
|
| * =============================================================================
|
| * SHORTCODE 4: Progress Bar Only
|
| * [compliance_progress_bar group_id="123" year="2025"]
|
| * =============================================================================
|
| */
|
| function bha_compliance_progress_bar_shortcode( $atts ) {
|
|
|
| $atts = shortcode_atts( array(
|
| 'group_id' => '',
|
| 'year' => '',
|
| ), $atts, 'compliance_progress_bar' );
|
|
|
| $group_id = ! empty( $atts['group_id'] ) ? absint( $atts['group_id'] ) : bha_compliance_get_user_group_id();
|
|
|
| if ( ! $group_id ) {
|
| return '';
|
| }
|
|
|
| $year = ! empty( $atts['year'] ) ? absint( $atts['year'] ) : (int) wp_date( 'Y' );
|
| if ( $year < 2020 || $year > 2050 ) {
|
| $year = (int) wp_date( 'Y' );
|
| }
|
|
|
| $tracker = bha_compliance_get_tracker( $group_id, $year );
|
|
|
| if ( ! $tracker ) {
|
| return '';
|
| }
|
|
|
| $data = bha_compliance_get_data( $tracker->ID );
|
|
|
| if ( isset( $data['error'] ) && $data['error'] ) {
|
| return '';
|
| }
|
|
|
| ob_start();
|
| echo bha_compliance_render_progress_bar( $data['totals'] );
|
| ?>
|
| <style>
|
| .compliance-progress { margin-bottom: 25px; }
|
| .progress-header { display: flex; justify-content: space-between; margin-bottom: 8px; }
|
| .progress-label { font-weight: 600; color:
|
| .progress-percentage { font-weight: 700; font-size: 18px; }
|
| .progress-bar-container { height: 24px; background:
|
| .progress-bar { height: 100%; border-radius: 12px; transition: width 0.5s ease; }
|
| .progress-good { background: linear-gradient(90deg,
|
| .progress-warning { background: linear-gradient(90deg,
|
| .progress-critical { background: linear-gradient(90deg,
|
| .progress-footer { margin-top: 8px; font-size: 14px; color:
|
| </style>
|
| <?php
|
| return ob_get_clean();
|
| }
|
| add_shortcode( 'compliance_progress_bar', 'bha_compliance_progress_bar_shortcode' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| function bha_compliance_status_badge_shortcode( $atts ) {
|
|
|
| $atts = shortcode_atts( array(
|
| 'group_id' => '',
|
| 'year' => '',
|
| ), $atts, 'compliance_status_badge' );
|
|
|
| $group_id = ! empty( $atts['group_id'] ) ? absint( $atts['group_id'] ) : bha_compliance_get_user_group_id();
|
|
|
| if ( ! $group_id ) {
|
| return '';
|
| }
|
|
|
| $year = ! empty( $atts['year'] ) ? absint( $atts['year'] ) : (int) wp_date( 'Y' );
|
| if ( $year < 2020 || $year > 2050 ) {
|
| $year = (int) wp_date( 'Y' );
|
| }
|
|
|
| $tracker = bha_compliance_get_tracker( $group_id, $year );
|
|
|
| if ( ! $tracker ) {
|
| return '';
|
| }
|
|
|
| $data = bha_compliance_get_data( $tracker->ID );
|
|
|
| if ( isset( $data['error'] ) && $data['error'] ) {
|
| return '';
|
| }
|
|
|
| $percentage = $data['totals']['percentage'];
|
|
|
| $bg_color = '#f8d7da';
|
| $text_color = '#721c24';
|
| $status_text = 'Critical';
|
|
|
| if ( $percentage >= 80 ) {
|
| $bg_color = '#d4edda';
|
| $text_color = '#155724';
|
| $status_text = 'Good';
|
| } elseif ( $percentage >= 50 ) {
|
| $bg_color = '#fff3cd';
|
| $text_color = '#856404';
|
| $status_text = 'Warning';
|
| }
|
|
|
| return '<span style="display:inline-block;padding:6px 12px;border-radius:20px;font-size:14px;font-weight:600;background:' . esc_attr( $bg_color ) . ';color:' . esc_attr( $text_color ) . ';">' . esc_html( $percentage ) . '% - ' . esc_html( $status_text ) . '</span>';
|
| }
|
| add_shortcode( 'compliance_status_badge', 'bha_compliance_status_badge_shortcode' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| function bha_compliance_debug_shortcode() {
|
|
|
| if ( ! current_user_can( 'manage_options' ) ) {
|
| return '<div style="padding:15px;background:#f8d7da;border:1px solid #f5c6cb;border-radius:4px;color:#721c24;">Admin access required.</div>';
|
| }
|
|
|
| $trackers = get_posts( array(
|
| 'post_type' => 'compliance_tracker',
|
| 'posts_per_page' => -1,
|
| 'post_status' => 'any',
|
| ) );
|
|
|
| ob_start();
|
| ?>
|
| <div style="background:#e7f3ff;padding:20px;margin:20px 0;border:3px solid #2196F3;border-radius:8px;">
|
| <h3 style="margin:0 0 15px;color:#1976D2;">Compliance Trackers Debug</h3>
|
| <p><strong>Total trackers found:</strong> <?php echo count( $trackers ); ?></p>
|
|
|
| <?php if ( ! empty( $trackers ) ) : ?>
|
| <?php foreach ( $trackers as $tracker ) : ?>
|
| <?php
|
| $group_id = get_field( 'group_id', $tracker->ID );
|
| $org_name = get_field( 'organization_name', $tracker->ID );
|
| $year = get_field( 'tracking_year', $tracker->ID );
|
| ?>
|
| <div style="background:#fff;padding:15px;margin:10px 0;border-left:4px solid #4CAF50;border-radius:4px;">
|
| <strong style="font-size:16px;color:#2196F3;">Post ID: <?php echo (int) $tracker->ID; ?></strong><br>
|
| <strong>Title:</strong> <?php echo esc_html( $tracker->post_title ); ?><br>
|
| <strong>Group ID:</strong> <?php echo $group_id ? esc_html( $group_id ) : 'Not set'; ?><br>
|
| <strong>Organization:</strong> <?php echo $org_name ? esc_html( $org_name ) : 'Not set'; ?><br>
|
| <strong>Year:</strong> <?php echo $year ? esc_html( $year ) : 'Not set'; ?><br>
|
| <div style="background:#fff3cd;padding:10px;margin-top:10px;">
|
| <strong>Test shortcodes:</strong><br>
|
| <code style="background:white;padding:2px 5px;display:block;margin:5px 0;">[compliance_dashboard group_id="<?php echo esc_attr( $group_id ); ?>" year="<?php echo esc_attr( $year ); ?>"]</code>
|
| <code style="background:white;padding:2px 5px;display:block;margin:5px 0;">[compliance_month_cards group_id="<?php echo esc_attr( $group_id ); ?>" year="<?php echo esc_attr( $year ); ?>"]</code>
|
| </div>
|
| </div>
|
| <?php endforeach; ?>
|
| <?php else : ?>
|
| <div style="background:#ffebee;padding:15px;border-left:4px solid #f44336;color:#c62828;">
|
| <strong>No compliance trackers found!</strong>
|
| </div>
|
| <?php endif; ?>
|
| </div>
|
| <?php
|
| return ob_get_clean();
|
| }
|
| add_shortcode( 'compliance_debug', 'bha_compliance_debug_shortcode' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| function bha_compliance_month_cards_css() {
|
| static $css_output = false;
|
| if ( $css_output ) {
|
| return '';
|
| }
|
| $css_output = true;
|
|
|
| ob_start();
|
| ?>
|
| <style>
|
| :root {
|
| --cc-primary:
|
| --cc-accent:
|
| --cc-teal:
|
| --cc-dark:
|
| --cc-good:
|
| --cc-warning:
|
| --cc-critical:
|
| --cc-future:
|
| --cc-current:
|
| --cc-white:
|
| --cc-bg:
|
| --cc-border:
|
| --cc-text:
|
| --cc-text-muted:
|
| }
|
|
|
| .cc-month-dashboard, .cc-month-detail { font-family: 'Poppins', 'Segoe UI', -apple-system, sans-serif; max-width: 1400px; margin: 0 auto; padding: 20px; }
|
| .cc-error { background:
|
|
|
|
|
| .cc-dashboard-header, .cc-detail-header { background: linear-gradient(135deg, var(--cc-primary) 0%, var(--cc-dark) 100%); border-radius: 20px; padding: 40px; margin-bottom: 40px; display: flex; justify-content: space-between; align-items: center; color: var(--cc-white); }
|
| .cc-org-title { font-size: 32px; font-weight: 700; margin: 0 0 8px 0; }
|
| .cc-year-subtitle, .cc-org-subtitle { font-size: 18px; opacity: 0.9; margin: 0; }
|
| .cc-overall-progress, .cc-progress-circle { background: rgba(255,255,255,0.15); border-radius: 16px; padding: 20px 30px; text-align: center; border: 1px solid rgba(255,255,255,0.2); }
|
| .cc-overall-pct, .cc-pct-value { display: block; font-size: 42px; font-weight: 700; line-height: 1; }
|
| .cc-overall-label, .cc-pct-label { font-size: 14px; opacity: 0.8; text-transform: uppercase; }
|
|
|
|
|
| .cc-month-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 24px; margin-bottom: 40px; }
|
| .cc-month-card { background: var(--cc-white); border-radius: 16px; padding: 24px; text-align: center; box-shadow: 0 4px 15px rgba(0,0,0,0.08); transition: all 0.3s ease; position: relative; overflow: hidden; border: 2px solid transparent; }
|
| .cc-month-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 5px; background: var(--cc-border); }
|
| .cc-month-card:hover { transform: translateY(-8px); box-shadow: 0 10px 40px rgba(0,0,0,0.12); }
|
|
|
| .cc-month-card.cc-status-good::before { background: linear-gradient(90deg, var(--cc-good),
|
| .cc-month-card.cc-status-warning::before { background: linear-gradient(90deg, var(--cc-warning),
|
| .cc-month-card.cc-status-critical::before { background: linear-gradient(90deg, var(--cc-critical),
|
| .cc-month-card.cc-status-current::before { background: linear-gradient(90deg, var(--cc-current),
|
| .cc-month-card.cc-status-current { border-color: var(--cc-current); }
|
| .cc-month-card.cc-quarterly, .cc-month-card.cc-annual { background: linear-gradient(180deg, var(--cc-white) 0%,
|
|
|
| .cc-card-badges { position: absolute; top: 12px; right: 12px; display: flex; gap: 6px; }
|
| .cc-badge { font-size: 10px; font-weight: 700; padding: 4px 8px; border-radius: 6px; text-transform: uppercase; }
|
| .cc-badge-quarterly { background: linear-gradient(135deg, var(--cc-accent), var(--cc-warning)); color: var(--cc-white); }
|
| .cc-badge-annual { background: linear-gradient(135deg, var(--cc-primary), var(--cc-dark)); color: var(--cc-white); }
|
|
|
| .cc-month-name { font-size: 20px; font-weight: 700; color: var(--cc-dark); margin-bottom: 16px; }
|
|
|
| .cc-progress-ring { width: 100px; height: 100px; margin: 0 auto 16px; position: relative; }
|
| .cc-progress-ring svg { transform: rotate(-90deg); width: 100%; height: 100%; }
|
| .cc-ring-bg { fill: none; stroke: var(--cc-border); stroke-width: 8; }
|
| .cc-ring-progress { fill: none; stroke: var(--cc-teal); stroke-width: 8; stroke-linecap: round; transition: stroke-dasharray 0.6s ease; }
|
| .cc-status-warning .cc-ring-progress { stroke: var(--cc-warning); }
|
| .cc-status-critical .cc-ring-progress { stroke: var(--cc-critical); }
|
| .cc-status-current .cc-ring-progress { stroke: var(--cc-current); }
|
| .cc-status-future .cc-ring-progress { stroke: var(--cc-future); }
|
| .cc-ring-text { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 22px; font-weight: 700; color: var(--cc-dark); }
|
|
|
| .cc-month-stats { display: flex; justify-content: center; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
|
| .cc-stat { font-size: 12px; color: var(--cc-text-muted); }
|
| .cc-stat-complete { color: var(--cc-good); }
|
| .cc-stat-overdue { color: var(--cc-critical); font-weight: 600; }
|
|
|
| .cc-view-btn { display: inline-block; padding: 10px 24px; background: linear-gradient(135deg, var(--cc-accent),
|
| .cc-view-btn:hover { transform: scale(1.05); box-shadow: 0 6px 20px rgba(245,132,76,0.4); }
|
|
|
| .cc-legend { display: flex; justify-content: center; gap: 24px; flex-wrap: wrap; padding: 20px; background: var(--cc-bg); border-radius: 12px; }
|
| .cc-legend-item { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--cc-text-muted); }
|
| .cc-dot { width: 12px; height: 12px; border-radius: 50%; }
|
| .cc-dot-good { background: var(--cc-good); }
|
| .cc-dot-warning { background: var(--cc-warning); }
|
| .cc-dot-critical { background: var(--cc-critical); }
|
| .cc-dot-current { background: var(--cc-current); }
|
|
|
|
|
| .cc-detail-header { margin-bottom: 30px; }
|
| .cc-detail-title h2 { font-size: 36px; font-weight: 700; margin: 0 0 8px 0; }
|
| .cc-progress-circle { width: 120px; height: 120px; border-radius: 50%; display: flex; flex-direction: column; justify-content: center; align-items: center; border: 4px solid rgba(255,255,255,0.3); }
|
|
|
| .cc-stats-bar { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-bottom: 30px; }
|
| .cc-stat-box { background: var(--cc-white); border-radius: 12px; padding: 24px; text-align: center; box-shadow: 0 1px 2px rgba(0,0,0,0.05); border-left: 4px solid var(--cc-border); }
|
| .cc-stat-box.cc-stat-completed { border-color: var(--cc-good); }
|
| .cc-stat-box.cc-stat-pending { border-color: var(--cc-warning); }
|
| .cc-stat-box.cc-stat-overdue { border-color: var(--cc-critical); }
|
| .cc-stat-box.cc-stat-total { border-color: var(--cc-dark); }
|
| .cc-stat-num { display: block; font-size: 32px; font-weight: 700; color: var(--cc-dark); line-height: 1; margin-bottom: 4px; }
|
| .cc-stat-completed .cc-stat-num { color: var(--cc-good); }
|
| .cc-stat-overdue .cc-stat-num { color: var(--cc-critical); }
|
| .cc-stat-lbl { font-size: 13px; color: var(--cc-text-muted); text-transform: uppercase; }
|
|
|
| .cc-special-badges { display: flex; gap: 16px; margin-bottom: 30px; flex-wrap: wrap; }
|
| .cc-special-badge { display: inline-flex; align-items: center; gap: 8px; padding: 12px 20px; border-radius: 10px; font-weight: 600; font-size: 14px; }
|
| .cc-special-quarterly { background: linear-gradient(135deg,
|
| .cc-special-annual { background: linear-gradient(135deg,
|
|
|
| .cc-items-grid { display: flex; flex-direction: column; gap: 12px; margin-bottom: 40px; }
|
| .cc-item-card { background: var(--cc-white); border-radius: 12px; padding: 20px 24px; display: flex; align-items: center; gap: 20px; box-shadow: 0 1px 2px rgba(0,0,0,0.05); border-left: 4px solid var(--cc-border); transition: all 0.3s ease; }
|
| .cc-item-card:hover { box-shadow: 0 4px 15px rgba(0,0,0,0.08); transform: translateX(4px); }
|
| .cc-item-complete { border-color: var(--cc-good); background: linear-gradient(90deg,
|
| .cc-item-pending { border-color: var(--cc-warning); }
|
| .cc-item-overdue { border-color: var(--cc-critical); background: linear-gradient(90deg,
|
| .cc-item-future { border-color: var(--cc-future); opacity: 0.7; }
|
|
|
| .cc-item-status { width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 20px; flex-shrink: 0; }
|
| .cc-item-complete .cc-item-status { background: var(--cc-good); color: var(--cc-white); }
|
| .cc-item-pending .cc-item-status { background:
|
| .cc-item-overdue .cc-item-status { background:
|
| .cc-item-future .cc-item-status { background: var(--cc-bg); color: var(--cc-future); }
|
|
|
| .cc-item-content { flex: 1; min-width: 0; }
|
| .cc-item-label { display: block; font-weight: 600; color: var(--cc-dark); font-size: 15px; }
|
| .cc-item-date { font-size: 13px; color: var(--cc-text-muted); }
|
| .cc-date-overdue { color: var(--cc-critical); font-weight: 500; }
|
|
|
| .cc-item-actions { flex-shrink: 0; }
|
| .cc-action-badge { display: inline-block; padding: 8px 16px; border-radius: 8px; font-size: 12px; font-weight: 600; text-transform: uppercase; }
|
| .cc-badge-done { background:
|
| .cc-badge-pending { background:
|
|
|
| .cc-back-section { text-align: center; padding-top: 20px; border-top: 1px solid var(--cc-border); }
|
| .cc-back-btn { display: inline-flex; align-items: center; gap: 8px; padding: 14px 28px; background: var(--cc-dark); color: var(--cc-white) !important; text-decoration: none !important; border-radius: 10px; font-weight: 600; transition: all 0.3s ease; }
|
| .cc-back-btn:hover { background: var(--cc-primary); transform: translateX(-4px); }
|
|
|
| @media (max-width: 768px) {
|
| .cc-dashboard-header, .cc-detail-header { flex-direction: column; text-align: center; gap: 24px; padding: 30px 20px; }
|
| .cc-org-title { font-size: 26px; }
|
| .cc-month-grid { grid-template-columns: repeat(3, 1fr); gap: 16px; }
|
| .cc-month-card { padding: 16px; }
|
| .cc-progress-ring { width: 80px; height: 80px; }
|
| .cc-ring-text { font-size: 18px; }
|
| .cc-stats-bar { grid-template-columns: repeat(2, 1fr); }
|
| .cc-item-card { flex-wrap: wrap; }
|
| }
|
| @media (max-width: 480px) {
|
| .cc-month-grid { grid-template-columns: repeat(2, 1fr); }
|
| .cc-stats-bar { grid-template-columns: 1fr; }
|
| }
|
| </style>
|
| <?php
|
| return ob_get_clean();
|
| }
|
| |
| |
Comments