| @@ -1,159 +1,516 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | /** |
| 3 | - * Payment Gateways Admin Page | |
| 3 | + * Integrations Admin Page | |
| 4 | 4 | * |
| 5 | - * Displays available payment gateway options as cards. | |
| 5 | + * Displays payment gateways and third-party integrations as cards. | |
| 6 | 6 | * |
| 7 | - * @package SpringDevs\Subscription | |
| 7 | + * @package SpringDevs\Subscription\Admin | |
| 8 | + * | |
| 9 | + * @var array $integrations Filtered integrations list. | |
| 8 | 10 | */ |
| 9 | 11 | |
| 10 | 12 | // Exit if accessed directly. |
| 11 | 13 | defined( 'ABSPATH' ) || exit; |
| 14 | + | |
| 15 | +// Split by type. | |
| 16 | +$payment_gateways = array_filter( | |
| 17 | + $integrations, | |
| 18 | + function ( $i ) { | |
| 19 | + return ( $i['type'] ?? 'payment_gateway' ) === 'payment_gateway'; | |
| 20 | + } | |
| 21 | +); | |
| 22 | +$third_party = array_filter( | |
| 23 | + $integrations, | |
| 24 | + function ( $i ) { | |
| 25 | + return ( $i['type'] ?? '' ) === 'third_party'; | |
| 26 | + } | |
| 27 | +); | |
| 28 | + | |
| 29 | +// Category config. The `section` key is the full sub-section heading; `label` is the short card badge. | |
| 30 | +$category_config = [ | |
| 31 | + 'lms' => [ | |
| 32 | + 'label' => __( 'LMS', 'subscription' ), | |
| 33 | + 'section' => __( 'Learning Management System', 'subscription' ), | |
| 34 | + 'bg' => '#ede9fe', | |
| 35 | + 'color' => '#6d28d9', | |
| 36 | + ], | |
| 37 | + 'crm' => [ | |
| 38 | + 'label' => __( 'CRM', 'subscription' ), | |
| 39 | + 'section' => __( 'Customer Relationship Management', 'subscription' ), | |
| 40 | + 'bg' => '#fce7f3', | |
| 41 | + 'color' => '#9d174d', | |
| 42 | + ], | |
| 43 | + 'automation' => [ | |
| 44 | + 'label' => __( 'Automation', 'subscription' ), | |
| 45 | + 'section' => __( 'Automation', 'subscription' ), | |
| 46 | + 'bg' => '#e0f2fe', | |
| 47 | + 'color' => '#0369a1', | |
| 48 | + ], | |
| 49 | + 'email' => [ | |
| 50 | + 'label' => __( 'Email', 'subscription' ), | |
| 51 | + 'section' => __( 'Email Marketing', 'subscription' ), | |
| 52 | + 'bg' => '#dcfce7', | |
| 53 | + 'color' => '#166534', | |
| 54 | + ], | |
| 55 | + 'license' => [ | |
| 56 | + 'label' => __( 'License', 'subscription' ), | |
| 57 | + 'section' => __( 'License Management', 'subscription' ), | |
| 58 | + 'bg' => '#fef3c7', | |
| 59 | + 'color' => '#92400e', | |
| 60 | + ], | |
| 61 | +]; | |
| 62 | + | |
| 63 | +// Group third-party integrations by category, preserving the config order above | |
| 64 | +// and appending any uncategorised ones under "Other". | |
| 65 | +$third_party_grouped = []; | |
| 66 | +foreach ( array_keys( $category_config ) as $cat_key ) { | |
| 67 | + $third_party_grouped[ $cat_key ] = []; | |
| 68 | +} | |
| 69 | +foreach ( $third_party as $integration ) { | |
| 70 | + $cat_key = $integration['category'] ?? ''; | |
| 71 | + $cat_key = isset( $category_config[ $cat_key ] ) ? $cat_key : 'other'; | |
| 72 | + $third_party_grouped[ $cat_key ][] = $integration; | |
| 73 | +} | |
| 74 | +$third_party_grouped = array_filter( $third_party_grouped ); | |
| 75 | + | |
| 76 | +/** | |
| 77 | + * Facet counts for the filter bar. | |
| 78 | + * | |
| 79 | + * Derived from the same array the cards render from, so a count can never | |
| 80 | + * disagree with what is on screen — including when filter_integration_actions() | |
| 81 | + * has removed something, or when a future integration is added and nobody | |
| 82 | + * remembers to update a hardcoded number. | |
| 83 | + */ | |
| 84 | +$subscrpt_status_of = static function ( array $integration ): string { | |
| 85 | + if ( ! empty( $integration['is_active'] ) ) { | |
| 86 | + return 'active'; | |
| 87 | + } | |
| 88 | + return ! empty( $integration['is_installed'] ) ? 'inactive' : 'not-installed'; | |
| 89 | +}; | |
| 90 | + | |
| 91 | +$subscrpt_facets = [ | |
| 92 | + 'category' => [], | |
| 93 | + 'status' => [ | |
| 94 | + 'active' => 0, | |
| 95 | + 'inactive' => 0, | |
| 96 | + 'not-installed' => 0, | |
| 97 | + ], | |
| 98 | + // No `recurring` facet: every integration that supports automatic | |
| 99 | + // recurring is a payment gateway, so the chip selected exactly the same | |
| 100 | + // six cards as the Payment Gateways category. The card badge still shows | |
| 101 | + // it, where it says something about that one integration. | |
| 102 | + 'tag' => [ | |
| 103 | + 'free' => 0, | |
| 104 | + 'pro' => 0, | |
| 105 | + 'beta' => 0, | |
| 106 | + ], | |
| 107 | +]; | |
| 108 | + | |
| 109 | +foreach ( $integrations as $integration ) { | |
| 110 | + $cat_key = ( ( $integration['type'] ?? '' ) === 'payment_gateway' ) | |
| 111 | + ? 'payment_gateway' | |
| 112 | + : ( isset( $category_config[ $integration['category'] ?? '' ] ) ? $integration['category'] : 'other' ); | |
| 113 | + | |
| 114 | + $subscrpt_facets['category'][ $cat_key ] = ( $subscrpt_facets['category'][ $cat_key ] ?? 0 ) + 1; | |
| 115 | + ++$subscrpt_facets['status'][ $subscrpt_status_of( $integration ) ]; | |
| 116 | + | |
| 117 | + if ( ! empty( $integration['is_pro'] ) ) { | |
| 118 | + ++$subscrpt_facets['tag']['pro']; | |
| 119 | + } else { | |
| 120 | + ++$subscrpt_facets['tag']['free']; | |
| 121 | + } | |
| 122 | + if ( ! empty( $integration['is_beta'] ) ) { | |
| 123 | + ++$subscrpt_facets['tag']['beta']; | |
| 124 | + } | |
| 125 | +} | |
| 126 | + | |
| 127 | +$subscrpt_total = count( $integrations ); | |
| 128 | + | |
| 129 | +// Labels for the category chips. Payment gateways are a `type`, not a | |
| 130 | +// `category`, but on this page they read as one more group. | |
| 131 | +// | |
| 132 | +// The short `label` rather than the full `section`: in a 232px rail | |
| 133 | +// "Customer Relationship Management" wraps to two lines and the group of | |
| 134 | +// chips ends up taller than the viewport. The section headings above the | |
| 135 | +// cards still spell it out in full. | |
| 136 | +$subscrpt_category_labels = [ 'payment_gateway' => __( 'Payment Gateways', 'subscription' ) ]; | |
| 137 | +foreach ( $category_config as $key => $cfg ) { | |
| 138 | + $subscrpt_category_labels[ $key ] = $cfg['label'] ?? $cfg['section']; | |
| 139 | +} | |
| 140 | +$subscrpt_category_labels['other'] = __( 'Other', 'subscription' ); | |
| 141 | + | |
| 142 | +$subscrpt_status_labels = [ | |
| 143 | + 'active' => __( 'Active', 'subscription' ), | |
| 144 | + 'inactive' => __( 'Inactive', 'subscription' ), | |
| 145 | + 'not-installed' => __( 'Not installed', 'subscription' ), | |
| 146 | +]; | |
| 147 | + | |
| 148 | +$subscrpt_tag_labels = [ | |
| 149 | + 'free' => __( 'Free', 'subscription' ), | |
| 150 | + 'pro' => __( 'Pro', 'subscription' ), | |
| 151 | + 'beta' => __( 'Beta', 'subscription' ), | |
| 152 | +]; | |
| 12 | 153 | ?> |
| 13 | 154 | |
| 155 | +<div class="wp-subscription-admin-content list-page"> | |
| 14 | 156 | |
| 15 | -<div class="wrap"> | |
| 157 | + <?php | |
| 158 | + wpsubs_render_page_header( | |
| 159 | + array( | |
| 160 | + 'title' => __( 'Integrations', 'subscription' ), | |
| 161 | + 'description' => __( 'Connect your subscriptions with payment gateways and third-party plugins.', 'subscription' ), | |
| 162 | + ) | |
| 163 | + ); | |
| 164 | + ?> | |
| 16 | 165 | |
| 17 | - <div class="wp-subscription-admin-content" style="max-width:1240px;margin:32px auto 0 auto"> | |
| 18 | - <h2><?php esc_html_e( 'Payment Gateways', 'subscription' ); ?></h2> | |
| 19 | - | |
| 20 | - <p style="font-size:14px;line-height:1.7;margin:0 0 24px 0;"> | |
| 21 | - <?php esc_html_e( 'Configure your store\'s payment gateways for subscription products. Enable, disable, and manage available payment methods that support recurring billing.', 'subscription' ); ?> | |
| 22 | - </p> | |
| 166 | + <?php | |
| 167 | + /** | |
| 168 | + * Filter bar. | |
| 169 | + * | |
| 170 | + * Filtering is done in the browser against cards that are all already in the | |
| 171 | + * DOM — there are fifteen of them, so a round trip per keystroke would be | |
| 172 | + * slower and would lose the page's scroll position for nothing. | |
| 173 | + * | |
| 174 | + * Every control degrades to "everything visible" without JavaScript, which | |
| 175 | + * is the state the page is in today. | |
| 176 | + */ | |
| 177 | + ?> | |
| 178 | + <?php | |
| 179 | + /* | |
| 180 | + * Two columns: filters in a sticky rail, results beside them. There are | |
| 181 | + * three facet groups and a dozen chips — as a horizontal bar above the | |
| 182 | + * grid that pushed the first card most of the way down the viewport, | |
| 183 | + * and scrolled out of reach exactly when a long list needed narrowing. | |
| 184 | + */ | |
| 185 | + ?> | |
| 186 | + <div class="subscrpt-int-layout"> | |
| 23 | 187 | |
| 24 | - <!-- Payment Gateway Cards --> | |
| 25 | - <div class="wp-subscription-payment-gateways" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:24px;margin-bottom:32px;"> | |
| 188 | + <aside class="subscrpt-int-sidebar"> | |
| 189 | + <div class="subscrpt-int-filters" data-subscrpt-integration-filters hidden> | |
| 190 | + | |
| 26 | 191 | <?php |
| 27 | - foreach ( $integrations as $integration ) : | |
| 192 | + $subscrpt_chip_groups = [ | |
| 193 | + [ | |
| 194 | + 'facet' => 'category', | |
| 195 | + 'legend' => __( 'Category', 'subscription' ), | |
| 196 | + 'counts' => $subscrpt_facets['category'], | |
| 197 | + 'labels' => $subscrpt_category_labels, | |
| 198 | + ], | |
| 199 | + [ | |
| 200 | + 'facet' => 'status', | |
| 201 | + 'legend' => __( 'Status', 'subscription' ), | |
| 202 | + 'counts' => $subscrpt_facets['status'], | |
| 203 | + 'labels' => $subscrpt_status_labels, | |
| 204 | + ], | |
| 205 | + [ | |
| 206 | + 'facet' => 'tag', | |
| 207 | + 'legend' => __( 'Tags', 'subscription' ), | |
| 208 | + 'counts' => $subscrpt_facets['tag'], | |
| 209 | + 'labels' => $subscrpt_tag_labels, | |
| 210 | + ], | |
| 211 | + ]; | |
| 212 | + | |
| 213 | + foreach ( $subscrpt_chip_groups as $subscrpt_group ) : | |
| 214 | + // A facet nothing has is noise, not information. | |
| 215 | + $subscrpt_shown = array_filter( $subscrpt_group['counts'] ); | |
| 216 | + if ( empty( $subscrpt_shown ) ) { | |
| 217 | + continue; | |
| 218 | + } | |
| 219 | + $subscrpt_is_single = in_array( $subscrpt_group['facet'], array( 'category', 'status' ), true ); | |
| 28 | 220 | ?> |
| 29 | - <div style="background-color:white;border-radius:8px;padding:18px;box-shadow:0 2px 8px #e0e7ef;"> | |
| 30 | - <div style="display: flex; gap: 14px;"> | |
| 31 | - <div> | |
| 32 | - <img | |
| 33 | - style="width: 40px; height: 40px; border-radius: 8px;" | |
| 34 | - src=<?php echo esc_url( $integration['icon_url'] ?? '' ); ?> | |
| 35 | - alt="" | |
| 36 | - /> | |
| 37 | - </div> | |
| 38 | - <div style="width: 100%;"> | |
| 39 | - <div style="display: flex; align-items: center; gap: 4px;"> | |
| 40 | - <span style="font-size:1.2em;font-weight:600;"> | |
| 41 | - <?php echo esc_html( $integration['title'] ?? '' ); ?> | |
| 42 | - </span> | |
| 221 | + <nav class="wpsubs-vnav" aria-label="<?php echo esc_attr( $subscrpt_group['legend'] ); ?>"> | |
| 222 | + <p class="wpsubs-vnav__title"><?php echo esc_html( $subscrpt_group['legend'] ); ?></p> | |
| 223 | + <?php | |
| 224 | + /* | |
| 225 | + * Category and status are single-select — a card has exactly | |
| 226 | + * one of each, so picking two could only ever return nothing. | |
| 227 | + * Both therefore need an explicit "All" to come back to. | |
| 228 | + */ | |
| 229 | + if ( $subscrpt_is_single ) : | |
| 230 | + ?> | |
| 231 | + <button type="button" class="wpsubs-vnav__item is-active" data-subscrpt-int-chip data-facet="<?php echo esc_attr( $subscrpt_group['facet'] ); ?>" data-value=""> | |
| 232 | + <span class="wpsubs-vnav__label"><?php esc_html_e( 'All', 'subscription' ); ?></span> | |
| 233 | + <span class="subscrpt-int-chip__count"><?php echo esc_html( number_format_i18n( $subscrpt_total ) ); ?></span> | |
| 234 | + </button> | |
| 235 | + <?php | |
| 236 | + endif; | |
| 237 | + ?> | |
| 238 | + <?php foreach ( $subscrpt_shown as $subscrpt_key => $subscrpt_count ) : ?> | |
| 239 | + <button | |
| 240 | + type="button" | |
| 241 | + class="wpsubs-vnav__item" | |
| 242 | + data-subscrpt-int-chip | |
| 243 | + data-facet="<?php echo esc_attr( $subscrpt_group['facet'] ); ?>" | |
| 244 | + data-value="<?php echo esc_attr( $subscrpt_key ); ?>" | |
| 245 | + aria-pressed="false" | |
| 246 | + > | |
| 247 | + <span class="wpsubs-vnav__label"><?php echo esc_html( $subscrpt_group['labels'][ $subscrpt_key ] ?? $subscrpt_key ); ?></span> | |
| 248 | + <span class="subscrpt-int-chip__count"><?php echo esc_html( number_format_i18n( $subscrpt_count ) ); ?></span> | |
| 249 | + </button> | |
| 250 | + <?php endforeach; ?> | |
| 251 | + </nav> | |
| 252 | + <?php | |
| 253 | + endforeach; | |
| 254 | + ?> | |
| 255 | + <?php // Last in the rail: it only appears once a filter is on, and it undoes what is above it. ?> | |
| 256 | + <button type="button" class="wpsubs-btn wpsubs-btn--outline wpsubs-btn--sm" data-subscrpt-int-reset hidden> | |
| 257 | + <?php esc_html_e( 'Reset', 'subscription' ); ?> | |
| 258 | + </button> | |
| 259 | + </div> | |
| 260 | + </aside> | |
| 43 | 261 | |
| 44 | - <div style="margin-left: auto; display: flex; gap: 6px; align-items: center;"> | |
| 45 | - <!-- Integration Status Badge --> | |
| 46 | - <?php if ( ! empty( $integration['is_installed'] ) && ! empty( $integration['is_active'] ) ) : ?> | |
| 47 | - <span style="font-size:0.85em;padding:4px 8px;border-radius:12px;white-space:nowrap;text-transform: uppercase;background:#e8f5e9;color:#1b5e20;"> | |
| 48 | - <?php esc_html_e( 'Active', 'subscription' ); ?> | |
| 262 | + <div class="subscrpt-int-results"> | |
| 263 | + <p class="subscrpt-int-empty" data-subscrpt-int-empty hidden> | |
| 264 | + <?php esc_html_e( 'No integrations match these filters.', 'subscription' ); ?> | |
| 265 | + </p> | |
| 266 | + | |
| 267 | + <?php if ( ! empty( $_GET['subscrpt_installed'] ) ) : // phpcs:ignore WordPress.Security.NonceVerification.Recommended ?> | |
| 268 | + <div class="notice notice-success is-dismissible" style="margin:0 0 16px;"><p><?php esc_html_e( 'Plugin installed and activated successfully.', 'subscription' ); ?></p></div> | |
| 269 | + <?php endif; ?> | |
| 270 | + | |
| 271 | + <!-- Payment Gateways --> | |
| 272 | + <div style="margin-bottom:32px;" data-subscrpt-int-section> | |
| 273 | + <div style="margin-bottom:12px;"> | |
| 274 | + <h2 style="font-size:12px;font-weight:600;color:var(--wpsubs-text-muted);text-transform:uppercase;letter-spacing:0.06em;margin:0;line-height:1.4;margin-left:1px;"><?php esc_html_e( 'Payment Gateways', 'subscription' ); ?></h2> | |
| 275 | + </div> | |
| 276 | + <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px;"> | |
| 277 | + <?php foreach ( $payment_gateways as $integration ) : ?> | |
| 278 | + <?php | |
| 279 | + $is_installed = ! empty( $integration['is_installed'] ); | |
| 280 | + $is_active = ! empty( $integration['is_active'] ); | |
| 281 | + $is_beta = ! empty( $integration['is_beta'] ); | |
| 282 | + $is_pro = ! empty( $integration['is_pro'] ); | |
| 283 | + $icon_url = $integration['icon_url'] ?? ''; | |
| 284 | + $icon_initial = $integration['icon_initial'] ?? strtoupper( substr( $integration['title'], 0, 2 ) ); | |
| 285 | + $icon_color = $integration['icon_color'] ?? '#64748b'; | |
| 286 | + | |
| 287 | + if ( $is_active ) { | |
| 288 | + $status_dot = '#16a34a'; | |
| 289 | + $status_text = __( 'Active', 'subscription' ); | |
| 290 | + } elseif ( $is_installed ) { | |
| 291 | + $status_dot = '#d97706'; | |
| 292 | + $status_text = __( 'Inactive', 'subscription' ); | |
| 293 | + } else { | |
| 294 | + $status_dot = '#9ca3af'; | |
| 295 | + $status_text = __( 'Not Installed', 'subscription' ); | |
| 296 | + } | |
| 297 | + | |
| 298 | + // Filtering reads these rather than scraping the rendered markup, | |
| 299 | + // which would break the moment a label is translated. | |
| 300 | + $subscrpt_card_status = $subscrpt_status_of( $integration ); | |
| 301 | + $subscrpt_card_tags = array_keys( | |
| 302 | + array_filter( | |
| 303 | + [ | |
| 304 | + 'free' => ! $is_pro, | |
| 305 | + 'pro' => $is_pro, | |
| 306 | + 'beta' => $is_beta, | |
| 307 | + 'recurring' => ! empty( $integration['supports_recurring'] ), | |
| 308 | + ] | |
| 309 | + ) | |
| 310 | + ); | |
| 311 | + ?> | |
| 312 | + <div | |
| 313 | + class="wpsubs-table-card subscrpt-int-card" | |
| 314 | + style="padding:16px;display:flex;flex-direction:column;gap:12px;" | |
| 315 | + data-subscrpt-int-card | |
| 316 | + data-name="<?php echo esc_attr( $integration['title'] ); ?>" | |
| 317 | + data-search="<?php echo esc_attr( strtolower( $integration['title'] . ' ' . ( $integration['description'] ?? '' ) ) ); ?>" | |
| 318 | + data-category="payment_gateway" | |
| 319 | + data-status="<?php echo esc_attr( $subscrpt_card_status ); ?>" | |
| 320 | + data-tags="<?php echo esc_attr( implode( ' ', $subscrpt_card_tags ) ); ?>" | |
| 321 | + > | |
| 322 | + | |
| 323 | + <!-- Header: icon + name + status --> | |
| 324 | + <div style="display:flex;gap:10px;align-items:flex-start;"> | |
| 325 | + <div style="width:40px;height:40px;flex-shrink:0;border-radius:8px;overflow:hidden;border:1px solid var(--wpsubs-border);background:var(--wpsubs-surface-muted);display:flex;align-items:center;justify-content:center;"> | |
| 326 | + <?php if ( $icon_url ) : ?> | |
| 327 | + <img src="<?php echo esc_url( $icon_url ); ?>" style="width:100%;height:100%;object-fit:contain;" alt="" /> | |
| 328 | + <?php else : ?> | |
| 329 | + <span style="font-size:11px;font-weight:700;color:#fff;background:<?php echo esc_attr( $icon_color ); ?>;width:100%;height:100%;display:flex;align-items:center;justify-content:center;"><?php echo esc_html( $icon_initial ); ?></span> | |
| 330 | + <?php endif; ?> | |
| 331 | + </div> | |
| 332 | + <div style="flex:1;min-width:0;"> | |
| 333 | + <div style="display:flex;align-items:flex-start;justify-content:space-between;gap:6px;margin-bottom:5px;"> | |
| 334 | + <span style="font-size:13px;font-weight:600;color:var(--wpsubs-text);line-height:1.3;"><?php echo esc_html( $integration['title'] ); ?></span> | |
| 335 | + <span style="display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:500;color:<?php echo esc_attr( $status_dot ); ?>;white-space:nowrap;flex-shrink:0;"> | |
| 336 | + <span style="width:7px;height:7px;border-radius:50%;background:<?php echo esc_attr( $status_dot ); ?>;flex-shrink:0;"></span> | |
| 337 | + <?php echo esc_html( $status_text ); ?> | |
| 338 | + </span> | |
| 339 | + </div> | |
| 340 | + <div style="display:flex;flex-wrap:wrap;gap:4px;"> | |
| 341 | + <?php if ( $is_pro ) : ?> | |
| 342 | + <span style="display:inline-flex;align-items:center;font-size:10px;font-weight:600;padding:2px 7px;border-radius:10px;background:var(--wpsubs-brand-light,#fff1eb);color:var(--wpsubs-brand-dark,#d93f00);line-height:1.6;"><?php esc_html_e( 'Pro', 'subscription' ); ?></span> | |
| 343 | + <?php endif; ?> | |
| 344 | + <?php if ( $is_beta ) : ?> | |
| 345 | + <span style="display:inline-flex;align-items:center;font-size:10px;font-weight:500;padding:2px 7px;border-radius:10px;background:#fff7ed;color:#c2410c;line-height:1.6;"><?php esc_html_e( 'Beta', 'subscription' ); ?></span> | |
| 346 | + <?php endif; ?> | |
| 347 | + <?php if ( ! empty( $integration['supports_recurring'] ) ) : ?> | |
| 348 | + <span style="display:inline-flex;align-items:center;gap:3px;font-size:10px;font-weight:500;padding:2px 7px;border-radius:10px;background:#dcfce7;color:#166534;line-height:1.6;"> | |
| 349 | + <svg width="10" height="10" viewBox="0 0 24 24" fill="none"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z" fill="#166534"/></svg> | |
| 350 | + <?php esc_html_e( 'Automatic recurring', 'subscription' ); ?> | |
| 49 | 351 | </span> |
| 50 | - <?php elseif ( ! empty( $integration['is_installed'] ) && empty( $integration['is_active'] ) ) : ?> | |
| 51 | - <span style="font-size:0.85em;padding:4px 8px;border-radius:12px;white-space:nowrap;text-transform: uppercase;background:#e3f2fd;color:#0d47a1;"> | |
| 52 | - <?php esc_html_e( 'Inactive', 'subscription' ); ?> | |
| 53 | - </span> | |
| 54 | 352 | <?php else : ?> |
| 55 | - <span style="font-size:0.85em;padding:4px 8px;border-radius:12px;white-space:nowrap;text-transform: uppercase;background:#ffebee;color:#b71c1c;"> | |
| 56 | - <?php esc_html_e( 'Not Available', 'subscription' ); ?> | |
| 57 | - </span> | |
| 353 | + <span style="display:inline-flex;align-items:center;font-size:10px;font-weight:500;padding:2px 7px;border-radius:10px;background:#fff3e0;color:#c2410c;line-height:1.6;"><?php esc_html_e( 'Manual renewals only', 'subscription' ); ?></span> | |
| 58 | 354 | <?php endif; ?> |
| 355 | + </div> | |
| 356 | + </div> | |
| 357 | + </div> | |
| 59 | 358 | |
| 60 | - <!-- Beta Badge --> | |
| 61 | - <?php if ( ! empty( $integration['is_beta'] ) && $integration['is_beta'] ) : ?> | |
| 62 | - <span style="font-size:0.85em;padding:4px 8px;border-radius:12px;white-space:nowrap;text-transform: uppercase;background:#ffebee;color:#b71c1c;font-weight: 500;"> | |
| 63 | - <?php esc_html_e( 'Beta', 'subscription' ); ?> | |
| 64 | - </span> | |
| 359 | + <!-- Description --> | |
| 360 | + <p style="font-size:13px;color:var(--wpsubs-text-muted);margin:0;line-height:1.5;"><?php echo esc_html( $integration['description'] ?? '' ); ?></p> | |
| 361 | + | |
| 362 | + <!-- Separator --> | |
| 363 | + <div style="border-top:1px solid var(--wpsubs-border);margin:auto -16px 0;"></div> | |
| 364 | + | |
| 365 | + <!-- Actions --> | |
| 366 | + <div style="display:flex;gap:6px;flex-wrap:wrap;"> | |
| 367 | + <?php if ( $is_pro && ! defined( 'SUBSCRIPT_PRO_VERSION' ) ) : ?> | |
| 368 | + <div style="width:100%;display:flex;align-items:center;gap:6px;background:var(--wpsubs-brand-light,#fff1eb);border-radius:6px;padding:7px 10px;font-size:12px;font-weight:500;color:var(--wpsubs-brand-dark,#d93f00);line-height:1.4;"> | |
| 369 | + <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="flex-shrink:0;"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg> | |
| 370 | + <?php esc_html_e( 'WPSubscription Pro required', 'subscription' ); ?> | |
| 371 | + </div> | |
| 372 | + <?php else : ?> | |
| 373 | + <?php | |
| 374 | + foreach ( $integration['actions'] as $action ) : | |
| 375 | + $is_primary = ( 'function' === $action['type'] ); | |
| 376 | + $btn_class = $is_primary ? 'wpsubs-btn wpsubs-btn--primary wpsubs-btn--sm' : 'wpsubs-btn wpsubs-btn--outline wpsubs-btn--sm'; | |
| 377 | + ?> | |
| 378 | + <?php if ( 'link' === $action['type'] ) : ?> | |
| 379 | + <a href="<?php echo esc_url( $action['url'] ); ?>" class="<?php echo esc_attr( $btn_class ); ?>"><?php echo esc_html( $action['label'] ); ?></a> | |
| 380 | + <?php elseif ( 'external_link' === $action['type'] ) : ?> | |
| 381 | + <a href="<?php echo esc_url( $action['url'] ); ?>" target="_blank" rel="noopener noreferrer" class="<?php echo esc_attr( $btn_class ); ?>"> | |
| 382 | + <?php echo esc_html( $action['label'] ); ?> | |
| 383 | + <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> | |
| 384 | + </a> | |
| 385 | + <?php elseif ( 'function' === $action['type'] ) : ?> | |
| 386 | + <button class="<?php echo esc_attr( $btn_class ); ?>" onclick="<?php echo esc_attr( $action['function'] ); ?>"><?php echo esc_html( $action['label'] ); ?></button> | |
| 65 | 387 | <?php endif; ?> |
| 388 | + <?php endforeach; ?> | |
| 389 | + <?php endif; ?> | |
| 390 | + </div> | |
| 391 | + | |
| 392 | + </div> | |
| 393 | + <?php endforeach; ?> | |
| 394 | + </div> | |
| 395 | + </div> | |
| 396 | + | |
| 397 | + <!-- 3rd Party Integrations --> | |
| 398 | + <?php foreach ( $third_party_grouped as $cat_key => $cat_integrations ) : ?> | |
| 399 | + <div style="margin-bottom:32px;" data-subscrpt-int-section> | |
| 400 | + <div style="margin-bottom:12px;"> | |
| 401 | + <h2 style="font-size:12px;font-weight:600;color:var(--wpsubs-text-muted);text-transform:uppercase;letter-spacing:0.06em;margin:0;line-height:1.4;margin-left:1px;"><?php echo esc_html( $category_config[ $cat_key ]['section'] ?? __( 'Other', 'subscription' ) ); ?></h2> | |
| 402 | + </div> | |
| 403 | + <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px;"> | |
| 404 | + <?php foreach ( $cat_integrations as $integration ) : ?> | |
| 405 | + <?php | |
| 406 | + $is_active = ! empty( $integration['is_active'] ); | |
| 407 | + $is_pro = ! empty( $integration['is_pro'] ); | |
| 408 | + $icon_url = $integration['icon_url'] ?? ''; | |
| 409 | + $icon_initial = $integration['icon_initial'] ?? strtoupper( substr( $integration['title'], 0, 2 ) ); | |
| 410 | + $icon_color = $integration['icon_color'] ?? '#64748b'; | |
| 411 | + $category = $integration['category'] ?? ''; | |
| 412 | + $cat = $category_config[ $category ] ?? [ | |
| 413 | + 'label' => $category, | |
| 414 | + 'bg' => '#f1f5f9', | |
| 415 | + 'color' => '#475569', | |
| 416 | + ]; | |
| 417 | + | |
| 418 | + $status_dot = $is_active ? '#16a34a' : '#9ca3af'; | |
| 419 | + $status_text = $is_active ? __( 'Active', 'subscription' ) : __( 'Not Installed', 'subscription' ); | |
| 420 | + | |
| 421 | + $subscrpt_card_status = $subscrpt_status_of( $integration ); | |
| 422 | + $subscrpt_card_tags = array_keys( | |
| 423 | + array_filter( | |
| 424 | + [ | |
| 425 | + 'free' => ! $is_pro, | |
| 426 | + 'pro' => $is_pro, | |
| 427 | + 'beta' => ! empty( $integration['is_beta'] ), | |
| 428 | + 'recurring' => ! empty( $integration['supports_recurring'] ), | |
| 429 | + ] | |
| 430 | + ) | |
| 431 | + ); | |
| 432 | + ?> | |
| 433 | + <div | |
| 434 | + class="wpsubs-table-card subscrpt-int-card" | |
| 435 | + style="padding:16px;display:flex;flex-direction:column;gap:12px;" | |
| 436 | + data-subscrpt-int-card | |
| 437 | + data-name="<?php echo esc_attr( $integration['title'] ); ?>" | |
| 438 | + data-search="<?php echo esc_attr( strtolower( $integration['title'] . ' ' . ( $integration['description'] ?? '' ) ) ); ?>" | |
| 439 | + data-category="<?php echo esc_attr( isset( $category_config[ $category ] ) ? $category : 'other' ); ?>" | |
| 440 | + data-status="<?php echo esc_attr( $subscrpt_card_status ); ?>" | |
| 441 | + data-tags="<?php echo esc_attr( implode( ' ', $subscrpt_card_tags ) ); ?>" | |
| 442 | + > | |
| 443 | + | |
| 444 | + <!-- Header: icon + name + status --> | |
| 445 | + <div style="display:flex;gap:10px;align-items:flex-start;"> | |
| 446 | + <div style="width:40px;height:40px;flex-shrink:0;border-radius:8px;overflow:hidden;border:1px solid var(--wpsubs-border);background:var(--wpsubs-surface-muted);display:flex;align-items:center;justify-content:center;"> | |
| 447 | + <?php if ( $icon_url ) : ?> | |
| 448 | + <img src="<?php echo esc_url( $icon_url ); ?>" style="width:100%;height:100%;object-fit:contain;" alt="" /> | |
| 449 | + <?php else : ?> | |
| 450 | + <span style="font-size:11px;font-weight:700;color:#fff;background:<?php echo esc_attr( $icon_color ); ?>;width:100%;height:100%;display:flex;align-items:center;justify-content:center;"><?php echo esc_html( $icon_initial ); ?></span> | |
| 451 | + <?php endif; ?> | |
| 452 | + </div> | |
| 453 | + <div style="flex:1;min-width:0;"> | |
| 454 | + <div style="display:flex;align-items:flex-start;justify-content:space-between;gap:6px;margin-bottom:5px;"> | |
| 455 | + <span style="font-size:13px;font-weight:600;color:var(--wpsubs-text);line-height:1.3;"><?php echo esc_html( $integration['title'] ); ?></span> | |
| 456 | + <span style="display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:500;color:<?php echo esc_attr( $status_dot ); ?>;white-space:nowrap;flex-shrink:0;"> | |
| 457 | + <span style="width:7px;height:7px;border-radius:50%;background:<?php echo esc_attr( $status_dot ); ?>;flex-shrink:0;"></span> | |
| 458 | + <?php echo esc_html( $status_text ); ?> | |
| 459 | + </span> | |
| 66 | 460 | </div> |
| 461 | + <?php if ( $cat['label'] || $is_pro ) : ?> | |
| 462 | + <div style="display:flex;flex-wrap:wrap;gap:4px;"> | |
| 463 | + <?php if ( $is_pro ) : ?> | |
| 464 | + <span style="display:inline-flex;align-items:center;font-size:10px;font-weight:600;padding:2px 7px;border-radius:10px;background:var(--wpsubs-brand-light,#fff1eb);color:var(--wpsubs-brand-dark,#d93f00);line-height:1.6;"><?php esc_html_e( 'Pro', 'subscription' ); ?></span> | |
| 465 | + <?php endif; ?> | |
| 466 | + <?php if ( $cat['label'] ) : ?> | |
| 467 | + <span style="display:inline-flex;align-items:center;font-size:10px;font-weight:500;padding:2px 7px;border-radius:10px;background:<?php echo esc_attr( $cat['bg'] ); ?>;color:<?php echo esc_attr( $cat['color'] ); ?>;line-height:1.6;"><?php echo esc_html( $cat['label'] ); ?></span> | |
| 468 | + <?php endif; ?> | |
| 469 | + </div> | |
| 470 | + <?php endif; ?> | |
| 67 | 471 | </div> |
| 68 | - <p style="font-size:12px;line-height:1.5;margin:8px 0 0 0;color:#555;"> | |
| 69 | - <?php echo esc_html( $integration['description'] ?? '' ); ?> | |
| 70 | - </p> | |
| 71 | 472 | </div> |
| 72 | - </div> | |
| 73 | 473 | |
| 74 | - <!-- Recurring Support --> | |
| 75 | - <?php if ( ! empty( $integration['supports_recurring'] ) ) : ?> | |
| 76 | - <div style="background:#e8f5e9;color:#1b5e20;padding:6px 10px;margin:20px 0;border-radius:4px;font-size:13px;margin-bottom:16px;"> | |
| 77 | - <span style="display:flex;align-items:center;gap:6px;"> | |
| 78 | - <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | |
| 79 | - <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z" fill="#1b5e20"/> | |
| 80 | - </svg> | |
| 81 | - <?php esc_html_e( 'Supports automatic recurring payments.', 'subscription' ); ?> | |
| 82 | - </span> | |
| 474 | + <!-- Description --> | |
| 475 | + <p style="font-size:13px;color:var(--wpsubs-text-muted);margin:0;line-height:1.5;"><?php echo esc_html( $integration['description'] ?? '' ); ?></p> | |
| 476 | + | |
| 477 | + <!-- Separator --> | |
| 478 | + <div style="border-top:1px solid var(--wpsubs-border);margin:auto -16px 0;"></div> | |
| 479 | + | |
| 480 | + <!-- Actions --> | |
| 481 | + <div style="display:flex;gap:6px;flex-wrap:wrap;"> | |
| 482 | + <?php if ( $is_pro && ! defined( 'SUBSCRIPT_PRO_VERSION' ) ) : ?> | |
| 483 | + <div style="width:100%;display:flex;align-items:center;gap:6px;background:var(--wpsubs-brand-light,#fff1eb);border-radius:6px;padding:7px 10px;font-size:12px;font-weight:500;color:var(--wpsubs-brand-dark,#d93f00);line-height:1.4;"> | |
| 484 | + <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="flex-shrink:0;"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg> | |
| 485 | + <?php esc_html_e( 'WPSubscription Pro required', 'subscription' ); ?> | |
| 486 | + </div> | |
| 487 | + <?php else : ?> | |
| 488 | + <?php | |
| 489 | + foreach ( $integration['actions'] as $action ) : | |
| 490 | + $is_primary = ( 'function' === $action['type'] ); | |
| 491 | + $btn_class = $is_primary ? 'wpsubs-btn wpsubs-btn--primary wpsubs-btn--sm' : 'wpsubs-btn wpsubs-btn--outline wpsubs-btn--sm'; | |
| 492 | + ?> | |
| 493 | + <?php if ( 'link' === $action['type'] ) : ?> | |
| 494 | + <a href="<?php echo esc_url( $action['url'] ); ?>" class="<?php echo esc_attr( $btn_class ); ?>"><?php echo esc_html( $action['label'] ); ?></a> | |
| 495 | + <?php elseif ( 'external_link' === $action['type'] ) : ?> | |
| 496 | + <a href="<?php echo esc_url( $action['url'] ); ?>" target="_blank" rel="noopener noreferrer" class="<?php echo esc_attr( $btn_class ); ?>"> | |
| 497 | + <?php echo esc_html( $action['label'] ); ?> | |
| 498 | + <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> | |
| 499 | + </a> | |
| 500 | + <?php elseif ( 'function' === $action['type'] ) : ?> | |
| 501 | + <button class="<?php echo esc_attr( $btn_class ); ?>" onclick="<?php echo esc_attr( $action['function'] ); ?>"><?php echo esc_html( $action['label'] ); ?></button> | |
| 502 | + <?php endif; ?> | |
| 503 | + <?php endforeach; ?> | |
| 504 | + <?php endif; ?> | |
| 83 | 505 | </div> |
| 84 | - <?php else : ?> | |
| 85 | - <div style="background:#fff3e0;color:#e65100;padding:6px 10px;margin:20px 0;border-radius:4px;font-size:13px;margin-bottom:16px;"> | |
| 86 | - <span style="display:flex;align-items:center;gap:6px;"> | |
| 87 | - <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | |
| 88 | - <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" fill="#e65100"/> | |
| 89 | - </svg> | |
| 90 | - <?php esc_html_e( 'Manual renewals only.', 'subscription' ); ?> | |
| 91 | - </span> | |
| 92 | - </div> | |
| 93 | - <?php endif; ?> | |
| 94 | 506 | |
| 95 | - <!-- Actions --> | |
| 96 | - <div style="width: 100%; display: flex; gap: 8px; flex-wrap: wrap; align-items: end;"> | |
| 97 | - <?php foreach ( $integration['actions'] as $integration_action ) : ?> | |
| 98 | - <?php if ( 'link' === $integration_action['type'] ) : ?> | |
| 99 | - <a href="<?php echo esc_url( $integration_action['url'] ); ?>" class="button button-primary" style="flex:1;text-align:center;"> | |
| 100 | - <?php echo esc_html( $integration_action['label'] ); ?> | |
| 101 | - </a> | |
| 102 | - <?php elseif ( 'external_link' === $integration_action['type'] ) : ?> | |
| 103 | - <a href="<?php echo esc_url( $integration_action['url'] ); ?>" target="_blank" class="button button-primary" style="flex:1;text-align:center;"> | |
| 104 | - <?php echo esc_html( $integration_action['label'] ); ?> | |
| 105 | - </a> | |
| 106 | - <?php elseif ( 'function' === $integration_action['type'] ) : ?> | |
| 107 | - <button | |
| 108 | - class="<?php echo esc_attr( $integration_action['class'] ?? 'button button-primary' ); ?>" | |
| 109 | - style="flex:1;text-align:center;" | |
| 110 | - onclick="<?php echo esc_attr( $integration_action['function'] ); ?>" | |
| 111 | - > | |
| 112 | - <?php echo esc_html( $integration_action['label'] ); ?> | |
| 113 | - </button> | |
| 114 | - <?php endif; ?> | |
| 115 | - <?php endforeach; ?> | |
| 116 | 507 | </div> |
| 117 | - </div> | |
| 118 | - <?php | |
| 119 | - endforeach; | |
| 120 | - ?> | |
| 508 | + <?php endforeach; ?> | |
| 509 | + </div> | |
| 121 | 510 | </div> |
| 511 | + <?php endforeach; ?> | |
| 122 | 512 | |
| 123 | - <!-- Information box --> | |
| 124 | - <div class="wp-subscription-admin-box" style="margin-bottom:24px;"> | |
| 125 | - <h3><?php esc_html_e( 'About Payment Gateways', 'subscription' ); ?></h3> | |
| 126 | - <p style="font-size:14px;line-height:1.7;margin:0 0 10px 0;"> | |
| 127 | - <?php esc_html_e( 'For subscription products to work properly, you need to use payment gateways that support recurring payments. Some payment methods only support manual renewals, which requires customers to manually pay for each renewal period.', 'subscription' ); ?> | |
| 128 | - </p> | |
| 129 | - <ul style="font-size:14px;line-height:1.6;margin:0 0 0 18px;padding:0;list-style:disc;"> | |
| 130 | - <li><?php esc_html_e( 'Automatic recurring billing requires a compatible payment gateway', 'subscription' ); ?></li> | |
| 131 | - <li><?php esc_html_e( 'Manual renewal methods work with any payment gateway', 'subscription' ); ?></li> | |
| 132 | - <li><?php esc_html_e( 'Some gateways may require additional configuration for subscriptions', 'subscription' ); ?></li> | |
| 133 | - </ul> | |
| 134 | 513 | </div> |
| 135 | 514 | |
| 136 | - <!-- Documentation box --> | |
| 137 | - <div class="wp-subscription-support-resources" style="display:grid;grid-template-columns:repeat(2,1fr);gap:24px;margin-bottom:24px;"> | |
| 138 | - <div class="wp-subscription-admin-box"> | |
| 139 | - <h3><?php esc_html_e( 'Payment Gateway Documentation', 'subscription' ); ?></h3> | |
| 140 | - <p style="font-size:14px;margin:0 0 8px 0;"> | |
| 141 | - <?php esc_html_e( 'Learn how to set up and configure payment gateways for subscription products.', 'subscription' ); ?> | |
| 142 | - </p> | |
| 143 | - <a href="https://docs.converslabs.com/en" target="_blank" class="button button-small" style="font-size:13px;padding:5px 14px;"> | |
| 144 | - <?php esc_html_e( 'View Documentation', 'subscription' ); ?> | |
| 145 | - </a> | |
| 146 | - </div> | |
| 147 | - <div class="wp-subscription-admin-box"> | |
| 148 | - <h3><?php esc_html_e( 'Need Help?', 'subscription' ); ?></h3> | |
| 149 | - <p style="font-size:14px;margin:0 0 8px 0;"> | |
| 150 | - <?php esc_html_e( 'If you\'re having trouble with a payment gateway, our support team can help.', 'subscription' ); ?> | |
| 151 | - </p> | |
| 152 | - <a href="https://wpsubscription.co/contact" target="_blank" class="button button-small" style="font-size:13px;padding:5px 14px;"> | |
| 153 | - <?php esc_html_e( 'Get Support', 'subscription' ); ?> | |
| 154 | - </a> | |
| 155 | - </div> | |
| 156 | - </div> | |
| 157 | 515 | </div> |
| 158 | - | |
| 159 | 516 | </div> |