block.json
5 months ago
index.asset.php
5 months ago
index.js
5 months ago
render.php
5 months ago
view.asset.php
5 months ago
view.js
5 months ago
render.php
302 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Render Book Page Reviews [Author Pro] Block |
| 4 | */ |
| 5 | |
| 6 | $block_id = isset($attributes['blockId']) ? $attributes['blockId'] : ''; |
| 7 | if (empty($block_id)) { |
| 8 | $block_id = 'awt-book-reviews-' . uniqid(); |
| 9 | } |
| 10 | |
| 11 | $anchor = isset($attributes['anchor']) ? $attributes['anchor'] : ''; |
| 12 | $element_id = !empty($anchor) ? $anchor : $block_id; |
| 13 | |
| 14 | $section_title = !empty($attributes['sectionTitle']) ? $attributes['sectionTitle'] : 'Customer Reviews'; |
| 15 | $form_title = !empty($attributes['formTitle']) ? $attributes['formTitle'] : 'Write a Review'; |
| 16 | $submit_btn_text = !empty($attributes['submitBtnText']) ? $attributes['submitBtnText'] : 'Submit Review'; |
| 17 | |
| 18 | $book_id = get_the_ID(); |
| 19 | $is_editor = defined('REST_REQUEST') && REST_REQUEST === true; |
| 20 | |
| 21 | // If in editor, preview with a real book if possible |
| 22 | if ($is_editor && get_post_type($book_id) !== 'book') { |
| 23 | $min_query = new WP_Query([ |
| 24 | 'post_type' => 'book', |
| 25 | 'posts_per_page' => 1, |
| 26 | 'post_status' => 'publish' |
| 27 | ]); |
| 28 | if ($min_query->have_posts()) { |
| 29 | $min_query->the_post(); |
| 30 | $book_id = get_the_ID(); |
| 31 | wp_reset_postdata(); |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | // 1. Fetch Reviews |
| 36 | $paged = (get_query_var('paged')) ? get_query_var('paged') : 1; |
| 37 | $args = array( |
| 38 | 'post_type' => 'book_reviews', |
| 39 | 'posts_per_page' => 5, |
| 40 | 'paged' => $paged, |
| 41 | 'meta_query' => array( |
| 42 | array( |
| 43 | 'key' => '_rswpbs_reviewed_book', |
| 44 | 'value' => $book_id, |
| 45 | 'compare' => '=' |
| 46 | ) |
| 47 | ) |
| 48 | ); |
| 49 | $reviews_query = new WP_Query($args); |
| 50 | $total_reviews = $reviews_query->found_posts; |
| 51 | |
| 52 | // 2. Calculate Stats (All time) |
| 53 | // Separate query for ALL reviews to calculate stats correctly |
| 54 | $stats_args = $args; |
| 55 | $stats_args['posts_per_page'] = -1; |
| 56 | $stats_args['paged'] = 1; |
| 57 | $all_reviews = get_posts($stats_args); |
| 58 | |
| 59 | $total_rating_sum = 0; |
| 60 | $star_counts = [5 => 0, 4 => 0, 3 => 0, 2 => 0, 1 => 0]; |
| 61 | |
| 62 | foreach ($all_reviews as $review) { |
| 63 | $rating = get_post_meta($review->ID, '_rswpbs_rating', true); |
| 64 | $rating = intval($rating); |
| 65 | if ($rating >= 1 && $rating <= 5) { |
| 66 | $total_rating_sum += $rating; |
| 67 | $star_counts[$rating]++; |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | $avg_rating = ($total_reviews > 0) ? round($total_rating_sum / count($all_reviews), 1) : 0; // Use count of fetched posts for accuracy |
| 72 | |
| 73 | // Helper to render stars |
| 74 | function awt_render_stars($rating) { |
| 75 | $html = ''; |
| 76 | |
| 77 | for ($i = 1; $i <= 5; $i++) { |
| 78 | $color_class = ($i <= $rating) ? 'text-yellow-400' : 'text-gray-300'; |
| 79 | |
| 80 | $html .= ' |
| 81 | <svg |
| 82 | xmlns="http://www.w3.org/2000/svg" |
| 83 | viewBox="0 0 640 640" |
| 84 | class="w-4 h-4 inline-block ' . esc_attr($color_class) . '" |
| 85 | fill="currentColor" |
| 86 | aria-hidden="true" |
| 87 | > |
| 88 | <path d="M341.5 45.1C337.4 37.1 329.1 32 320.1 32C311.1 32 302.8 37.1 298.7 45.1L225.1 189.3L65.2 214.7C56.3 216.1 48.9 222.4 46.1 231C43.3 239.6 45.6 249 51.9 255.4L166.3 369.9L141.1 529.8C139.7 538.7 143.4 547.7 150.7 553C158 558.3 167.6 559.1 175.7 555L320.1 481.6L464.4 555C472.4 559.1 482.1 558.3 489.4 553C496.7 547.7 500.4 538.8 499 529.8L473.7 369.9L588.1 255.4C594.5 249 596.7 239.6 593.9 231C591.1 222.4 583.8 216.1 574.8 214.7L415 189.3L341.5 45.1z"/> |
| 89 | </svg>'; |
| 90 | } |
| 91 | |
| 92 | return $html; |
| 93 | } |
| 94 | |
| 95 | |
| 96 | $wrapper_attributes = get_block_wrapper_attributes([ |
| 97 | 'class' => 'awt-book-reviews', |
| 98 | 'id' => $element_id, |
| 99 | ]); |
| 100 | ?> |
| 101 | |
| 102 | <section class="py-20 bg-white" <?php echo $wrapper_attributes; ?>> |
| 103 | <div class="awt-container"> |
| 104 | <div class="grid grid-cols-1 lg:grid-cols-12 gap-12"> |
| 105 | |
| 106 | <!-- Left Column: Stats --> |
| 107 | <div class="lg:col-span-4"> |
| 108 | <!-- Summary Card --> |
| 109 | <div class="bg-paper p-8 rounded-xl border border-gray-100 text-center mb-8"> |
| 110 | <div class="text-5xl font-bold text-primary mb-2"><?php echo esc_html($avg_rating); ?></div> |
| 111 | <div class="flex justify-center gap-1 text-yellow-400 mb-2"> |
| 112 | <?php echo awt_render_stars(round($avg_rating)); ?> |
| 113 | </div> |
| 114 | <p class="text-secondary text-sm">Based on <?php echo esc_html($total_reviews); ?> reviews</p> |
| 115 | </div> |
| 116 | |
| 117 | <!-- Progress Bars --> |
| 118 | <div class="space-y-3 mb-8"> |
| 119 | <?php for ($star = 5; $star >= 1; $star--) : |
| 120 | $count = $star_counts[$star]; |
| 121 | $percent = ($total_reviews > 0) ? ($count / $total_reviews) * 100 : 0; |
| 122 | ?> |
| 123 | <div class="flex items-center text-sm"> |
| 124 | <span class="w-12 text-secondary font-medium"><?php echo $star; ?> Star</span> |
| 125 | <div class="flex-1 h-2 bg-gray-100 rounded-full mx-3 overflow-hidden"> |
| 126 | <div class="h-full bg-yellow-400 rounded-full" style="width: <?php echo esc_attr($percent); ?>%"></div> |
| 127 | </div> |
| 128 | <span class="w-8 text-right text-gray-400 text-xs"><?php echo round($percent); ?>%</span> |
| 129 | </div> |
| 130 | <?php endfor; ?> |
| 131 | </div> |
| 132 | |
| 133 | <!-- CTA Box --> |
| 134 | <div class="bg-primary text-white p-6 rounded-xl relative overflow-hidden"> |
| 135 | <div class="relative z-10"> |
| 136 | <h3 class="font-serif font-bold text-xl mb-2">Share your thoughts</h3> |
| 137 | <p class="text-white/80 text-sm mb-6">If you've read this book, we'd love to hear what you think!</p> |
| 138 | <a href="#write-review" class="block w-full py-3 bg-white text-primary text-center font-bold rounded-lg hover:bg-accent hover:text-white transition">Write a Review</a> |
| 139 | </div> |
| 140 | <!-- Decorative Circles in CSS/Class similar to ref if needed, strictly keeping structure simple as requested --> |
| 141 | </div> |
| 142 | </div> |
| 143 | |
| 144 | <!-- Right Column: Review List --> |
| 145 | <div class="lg:col-span-8"> |
| 146 | <div class="flex justify-between items-center mb-8 pb-4 border-b border-gray-100"> |
| 147 | <h2 class="font-serif font-bold text-2xl text-primary"><?php echo esc_html($section_title); ?></h2> |
| 148 | |
| 149 | <!-- Sorting Dropdown (Optional visual only for now) --> |
| 150 | <div class="relative"> |
| 151 | <select class="appearance-none bg-white border border-gray-200 rounded-lg py-2 pl-4 pr-10 text-sm font-medium text-secondary focus:outline-none focus:border-accent"> |
| 152 | <option>Most Recent</option> |
| 153 | <option>Highest Rated</option> |
| 154 | <option>Lowest Rated</option> |
| 155 | </select> |
| 156 | <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-500"> |
| 157 | <svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"/></svg> |
| 158 | </div> |
| 159 | </div> |
| 160 | </div> |
| 161 | |
| 162 | <?php if ($reviews_query->have_posts()) : ?> |
| 163 | <div class="space-y-8 mb-12"> |
| 164 | <?php while ($reviews_query->have_posts()) : $reviews_query->the_post(); |
| 165 | $reviewer_email = get_post_meta(get_the_ID(), '_rswpbs_reviewer_email', true); |
| 166 | $reviewer_name = get_post_meta(get_the_ID(), '_rswpbs_reviewer_name', true); |
| 167 | $rating = get_post_meta(get_the_ID(), '_rswpbs_rating', true); |
| 168 | $avatar_url = get_avatar_url($reviewer_email, ['default' => 'mp']); |
| 169 | ?> |
| 170 | <div class="flex gap-4 mb-8"> |
| 171 | <div class="flex-shrink-0"> |
| 172 | <img src="<?php echo esc_url($avatar_url); ?>" alt="<?php echo esc_attr($reviewer_name); ?>" class="w-12 h-12 rounded-full object-cover border border-gray-100"> |
| 173 | </div> |
| 174 | <div class="flex-grow"> |
| 175 | <div class="flex justify-between items-start mb-2"> |
| 176 | <div> |
| 177 | <h4 class="font-bold text-primary"><?php echo esc_html($reviewer_name); ?></h4> |
| 178 | <div class="text-xs text-gray-400"><?php echo get_the_date(); ?></div> |
| 179 | </div> |
| 180 | <div class="flex text-yellow-400 text-xs gap-0.5"> |
| 181 | <?php echo awt_render_stars(intval($rating)); ?> |
| 182 | </div> |
| 183 | </div> |
| 184 | <h5 class="font-bold text-sm text-primary mb-2"><?php the_title(); ?></h5> |
| 185 | <div class="text-secondary text-sm leading-relaxed"> |
| 186 | <?php the_content(); ?> |
| 187 | <!-- Using the_content() or excerpt depending on need. The ref request asked for content. --> |
| 188 | </div> |
| 189 | </div> |
| 190 | </div> |
| 191 | <?php endwhile; wp_reset_postdata(); ?> |
| 192 | </div> |
| 193 | |
| 194 | <!-- Pagination --> |
| 195 | <div class="flex justify-center gap-2 mb-12"> |
| 196 | <?php |
| 197 | $prev_icon = ' |
| 198 | <svg xmlns="http://www.w3.org/2000/svg" |
| 199 | viewBox="0 0 640 640" |
| 200 | class="w-4 h-4" |
| 201 | fill="currentColor" |
| 202 | aria-hidden="true"> |
| 203 | <path d="M169.4 297.4C156.9 309.9 156.9 330.2 169.4 342.7L361.4 534.7C373.9 547.2 394.2 547.2 406.7 534.7C419.2 522.2 419.2 501.9 406.7 489.4L237.3 320L406.6 150.6C419.1 138.1 419.1 117.8 406.6 105.3C394.1 92.8 373.8 92.8 361.3 105.3L169.3 297.3z"/> |
| 204 | </svg>'; |
| 205 | |
| 206 | $next_icon = ' |
| 207 | <svg xmlns="http://www.w3.org/2000/svg" |
| 208 | viewBox="0 0 640 640" |
| 209 | class="w-4 h-4" |
| 210 | fill="currentColor" |
| 211 | aria-hidden="true"> |
| 212 | <path d="M471.1 297.4C483.6 309.9 483.6 330.2 471.1 342.7L279.1 534.7C266.6 547.2 246.3 547.2 233.8 534.7C221.3 522.2 221.3 501.9 233.8 489.4L403.2 320L233.9 150.6C221.4 138.1 221.4 117.8 233.9 105.3C246.4 92.8 266.7 92.8 279.2 105.3L471.2 297.3z"/> |
| 213 | </svg>'; |
| 214 | |
| 215 | $pagination = paginate_links(array( |
| 216 | 'base' => str_replace(999999999, '%#%', esc_url(get_pagenum_link(999999999))), |
| 217 | 'format' => '?paged=%#%', |
| 218 | 'current' => max(1, get_query_var('paged')), |
| 219 | 'total' => $reviews_query->max_num_pages, |
| 220 | 'prev_text' => $prev_icon, |
| 221 | 'next_text' => $next_icon, |
| 222 | 'type' => 'array', |
| 223 | )); |
| 224 | |
| 225 | |
| 226 | if( is_array( $pagination ) ) { |
| 227 | foreach ( $pagination as $page ) { |
| 228 | // Add tailwind classes to pagination links logic if needed, |
| 229 | // but standard WP output is usually sufficient with theme styles, |
| 230 | // or we can wrapper it. |
| 231 | // For now echoing simple links. |
| 232 | echo '<span class="px-4 py-2 border border-gray-200 rounded hover:bg-gray-50 text-sm font-medium text-secondary transition cursor-pointer">' . $page . '</span>'; |
| 233 | } |
| 234 | } |
| 235 | ?> |
| 236 | </div> |
| 237 | |
| 238 | <?php else : ?> |
| 239 | <p class="text-gray-500 italic mb-12">No reviews yet. Be the first to review!</p> |
| 240 | <?php endif; ?> |
| 241 | |
| 242 | <!-- Submission Form --> |
| 243 | <div id="write-review" class="pt-12 border-t border-gray-100"> |
| 244 | <h3 class="font-serif font-bold text-2xl text-primary mb-6"><?php echo esc_html($form_title); ?></h3> |
| 245 | |
| 246 | <form id="rswpbs-review-form" action="" method="post" class="space-y-6"> |
| 247 | <?php |
| 248 | // AJAX Action and Nonce for RS WP Books Showcase |
| 249 | // Note: The plugin must handle this action. |
| 250 | wp_nonce_field( 'rswpbs_submit_review', '_rswpbs_submit_review_nonce' ); |
| 251 | ?> |
| 252 | <input type="hidden" name="action" value="rswpbs_submit_review_form"> |
| 253 | <input type="hidden" name="current_post_id" value="<?php echo esc_attr($book_id); ?>"> |
| 254 | <input type="hidden" name="rating" value="5"> |
| 255 | |
| 256 | <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
| 257 | <div> |
| 258 | <label class="block text-sm font-bold text-primary mb-2">Name *</label> |
| 259 | <input type="text" name="reviewer_name" required class="w-full px-4 py-3 bg-paper border border-gray-200 rounded-lg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent transition"> |
| 260 | </div> |
| 261 | <div> |
| 262 | <label class="block text-sm font-bold text-primary mb-2">Email *</label> |
| 263 | <input type="email" name="reviewer_email" required class="w-full px-4 py-3 bg-paper border border-gray-200 rounded-lg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent transition"> |
| 264 | </div> |
| 265 | </div> |
| 266 | |
| 267 | <div> |
| 268 | <label class="block text-sm font-bold text-primary mb-2">Rating *</label> |
| 269 | <div class="flex gap-1 text-2xl awt-star-rating-select"> |
| 270 | <?php for ($i = 1; $i <= 5; $i++) : ?> |
| 271 | <svg |
| 272 | class="awt-star-icon w-6 h-6 cursor-pointer text-gray-300 hover:scale-110 transition-transform duration-200" |
| 273 | data-rating="<?php echo $i; ?>" |
| 274 | fill="currentColor" |
| 275 | xmlns="http://www.w3.org/2000/svg" |
| 276 | viewBox="0 0 640 640"> |
| 277 | <path d="M341.5 45.1C337.4 37.1 329.1 32 320.1 32C311.1 32 302.8 37.1 298.7 45.1L225.1 189.3L65.2 214.7C56.3 216.1 48.9 222.4 46.1 231C43.3 239.6 45.6 249 51.9 255.4L166.3 369.9L141.1 529.8C139.7 538.7 143.4 547.7 150.7 553C158 558.3 167.6 559.1 175.7 555L320.1 481.6L464.4 555C472.4 559.1 482.1 558.3 489.4 553C496.7 547.7 500.4 538.8 499 529.8L473.7 369.9L588.1 255.4C594.5 249 596.7 239.6 593.9 231C591.1 222.4 583.8 216.1 574.8 214.7L415 189.3L341.5 45.1z"/> |
| 278 | </svg> |
| 279 | <?php endfor; ?> |
| 280 | </div> |
| 281 | </div> |
| 282 | |
| 283 | <div> |
| 284 | <label class="block text-sm font-bold text-primary mb-2">Review Title *</label> |
| 285 | <input type="text" name="review_title" required class="w-full px-4 py-3 bg-paper border border-gray-200 rounded-lg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent transition"> |
| 286 | </div> |
| 287 | |
| 288 | <div> |
| 289 | <label class="block text-sm font-bold text-primary mb-2">Your Review *</label> |
| 290 | <textarea name="reviewer_comment" rows="5" required class="w-full px-4 py-3 bg-paper border border-gray-200 rounded-lg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent transition"></textarea> |
| 291 | </div> |
| 292 | |
| 293 | <button type="submit" class="px-8 py-3 bg-accent text-white font-bold rounded-lg hover:border-orange-700 hover:bg-orange-700 transition shadow-lg shadow-orange-500/20"> |
| 294 | <?php echo esc_html($submit_btn_text); ?> |
| 295 | </button> |
| 296 | </form> |
| 297 | </div> |
| 298 | </div> |
| 299 | </div> |
| 300 | </div> |
| 301 | </section> |
| 302 |