- 
                Notifications
    
You must be signed in to change notification settings  - Fork 152
 
Enhancement/tailwind subscription packs view 993 #1689
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enhancement/tailwind subscription packs view 993 #1689
Conversation
| 
          
 Warning Rate limit exceeded@arifulhoque7 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 3 minutes and 7 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the  We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ⛔ Files ignored due to path filters (1)
 📒 Files selected for processing (1)
 WalkthroughDisables legacy  Changes
 Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related issues
 Possibly related PRs
 Suggested labels
 Suggested reviewers
 Poem
 Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
 ✅ Passed checks (3 passed)
 Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment   | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 16
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️  Outside diff range comments (1)
assets/js/components/Subscriptions.vue (1)
75-83: Handle “preferences” in goToList to avoid invalid status fetch.Route directly to Preferences instead of calling setSubscriptionsByStatus('preferences').
const goToList = () => { subscriptionStore.isDirty = false; subscriptionStore.isUnsavedPopupOpen = false; - subscriptionStore.setSubscriptionsByStatus( tempSubscriptionStatus.value ); - componentStore.setCurrentComponent( 'List' ); - subscriptionStore.setCurrentSubscription(null); - subscriptionStore.currentPageNumber = 1; + if (tempSubscriptionStatus.value === 'preferences') { + componentStore.setCurrentComponent('Preferences'); + subscriptionStore.currentSubscriptionStatus = 'preferences'; + } else { + subscriptionStore.setSubscriptionsByStatus(tempSubscriptionStatus.value); + componentStore.setCurrentComponent('List'); + subscriptionStore.setCurrentSubscription(null); + subscriptionStore.currentPageNumber = 1; + } };
🧹 Nitpick comments (18)
assets/js/stores/notice.js (1)
9-15: Consider customizable durations and IDs.Optional: support addNotice(notice, { duration = 3000, id }) and remove by id/reference to avoid index coupling.
includes/Api/Subscription.php (2)
101-116: Add param schema/validation to the subscription-settings endpoint.Define args to validate button_color at the REST layer.
Apply:
register_rest_route( $this->namespace, '/subscription-settings', [ [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_subscription_settings' ], 'permission_callback' => [ $this, 'permission_check' ], ], [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [ $this, 'update_subscription_settings' ], 'permission_callback' => [ $this, 'permission_check' ], + 'args' => [ + 'button_color' => [ + 'type' => 'string', + 'required' => true, + 'validate_callback' => function( $value ) { + return (bool) sanitize_hex_color( $value ); + }, + ], + ], ], ] );
559-565: Silence PHPMD for unused $request or use it.Either use $request or suppress the warning for this callback.
Apply one:
- public function get_subscription_settings( $request ) { + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function get_subscription_settings( $request ) {or
public function get_subscription_settings( $request ) { + // $request kept for signature compatibility.src/css/admin/form-builder.css (1)
188-222: Avoid hardcoded “green” for range inputs; align with primary colorThese hardcoded greens will drift from branding. Use the primary hex or a CSS var.
- background: green; + background: #059669; /* match theme.colors.primary */ @@ - background: green; + background: #059669; @@ - background: green; + background: #059669; @@ - background: green; + background: #059669;Or define
:root { --wpuf-primary: #059669; }once and referencevar(--wpuf-primary).includes/Frontend.php (1)
162-166: Good: localized to the chosen handle; minor DRY improvementLooks correct. To avoid recomputing the shortcode check, reuse the
$has_sub_pack_shortcodevariable.- $subscription_script_handle = wpuf_has_shortcode( 'wpuf_sub_pack' ) ? 'wpuf-frontend-subscriptions' : 'wpuf-subscriptions'; + $subscription_script_handle = ! empty( $has_sub_pack_shortcode ) ? 'wpuf-frontend-subscriptions' : 'wpuf-subscriptions';assets/js/components/subscriptions/SidebarMenu.vue (2)
44-47: Guard against undefined counts to avoid NaN coercion and improve clarityUse a default value when computing the badge condition.
- v-if="subscriptionStore.allCount[Object.keys( item )[0]] > 0" + v-if="(subscriptionStore.allCount[Object.keys( item )[0]] || 0) > 0"
25-28: Remove no-op status.map blockThis map’s result is unused; it adds noise.
-status.map( ( item ) => { - const key = Object.keys( item )[0]; - const label = item[key]; -} );assets/js/components/Subscriptions.vue (1)
50-73: Avoid potential race/flicker: await list fetch before swapping component.Make checkIsDirty async and await setSubscriptionsByStatus to keep UI/state in sync.
-const checkIsDirty = ( subscriptionStatus = 'all' ) => { +const checkIsDirty = async ( subscriptionStatus = 'all' ) => { if (subscriptionStatus === 'preferences') { // Handle preferences separately if (subscriptionStore.isDirty) { subscriptionStore.isUnsavedPopupOpen = true; tempSubscriptionStatus.value = subscriptionStatus; } else { componentStore.setCurrentComponent( 'Preferences' ); subscriptionStore.currentSubscriptionStatus = 'preferences'; } } else if (subscriptionStore.isDirty) { subscriptionStore.isUnsavedPopupOpen = true; tempSubscriptionStatus.value = subscriptionStatus; } else { subscriptionStore.isDirty = false; subscriptionStore.isUnsavedPopupOpen = false; - subscriptionStore.setSubscriptionsByStatus( subscriptionStatus ); + await subscriptionStore.setSubscriptionsByStatus( subscriptionStatus ); componentStore.setCurrentComponent( 'List' ); subscriptionStore.setCurrentSubscription(null); subscriptionStore.getSubscriptionCount(); subscriptionStore.currentPageNumber = 1; } };templates/subscriptions/listing.php (1)
16-26: Inline CSS for FOUC: consider moving to the built CSS.Prefer bundling these under src/css/frontend/subscriptions.css as “critical” first rules or preloading CSS to avoid CSP/inline-style restrictions.
vite.config.mjs (1)
10-13: Guard against invalid ENTRY values to fail fast.If ENTRY is set but not in entries, Rollup will get undefined and error later. Throw early with a clear message.
-export default defineConfig(() => { - const entryPoint = process.env.ENTRY; - const input = entryPoint ? { [entryPoint]: entries[entryPoint] } : entries; +export default defineConfig(() => { + const entryPoint = process.env.ENTRY; + if (entryPoint && !entries[entryPoint]) { + throw new Error(`Unknown ENTRY "${entryPoint}". Valid entries: ${Object.keys(entries).join(', ')}`); + } + const input = entryPoint ? { [entryPoint]: entries[entryPoint] } : entries;assets/js/components/subscriptions/Preferences.vue (4)
4-5: Remove unused subscription store import/instance
useSubscriptionStoreandsubscriptionStoreare unused.-import { useSubscriptionStore } from '../../stores/subscription'; import { useNoticeStore } from '../../stores/notice'; @@ -const subscriptionStore = useSubscriptionStore(); const noticeStore = useNoticeStore();Also applies to: 7-8
19-34: Surface load failures to users and handle non-OK responsesCurrently failures only log to console. Add a notice on non-OK or network failure.
- if (response.ok) { + if (response.ok) { const data = await response.json(); settings.value = { ...settings.value, ...data }; - } + } else { + noticeStore.addNotice({ + type: 'error', + message: __('Failed to load settings', 'wp-user-frontend') + }); + } } catch (error) { - console.error('Error loading settings:', error); + console.error('Error loading settings:', error); + noticeStore.addNotice({ + type: 'error', + message: __('Failed to load settings', 'wp-user-frontend') + }); }
107-111: Constrain free-text color inputAdd HTML validation hints to reduce bad data entry.
- <input + <input v-model="settings.button_color" type="text" - class="wpuf-rounded-md wpuf-border-gray-300 wpuf-shadow-sm focus:wpuf-border-primary focus:wpuf-ring-primary wpuf-text-sm wpuf-w-32" + class="wpuf-rounded-md wpuf-border-gray-300 wpuf-shadow-sm focus:wpuf-border-primary focus:wpuf-ring-primary wpuf-text-sm wpuf-w-32" + pattern="^#[0-9a-fA-F]{6}$" + maxlength="7" + inputmode="text" + placeholder="#4f46e5" + spellcheck="false" />
113-121: Avoid inline DOM event handlers; use CSS hover insteadInline
onmouseover/onmouseoutis brittle in Vue templates. Prefer a CSS hover rule.- <button + <button type="button" :style="{ backgroundColor: settings.button_color }" class="wpuf-subscription-buy-btn wpuf-block wpuf-w-full wpuf-rounded-md wpuf-px-3 wpuf-py-2 wpuf-text-center wpuf-text-sm wpuf-font-semibold wpuf-text-white wpuf-shadow-sm wpuf-ring-0 wpuf-transition-all wpuf-duration-200 wpuf-leading-6" - onmouseover="this.style.filter='brightness(0.9)'" - onmouseout="this.style.filter='brightness(1)'"> + > {{ __('Buy Now', 'wp-user-frontend') }} </button>Add a scoped style at the end of the file:
+</template> + +<style scoped> +.wpuf-subscription-buy-btn:hover { + filter: brightness(0.9); +} +</style>templates/subscriptions/pack-details.php (4)
53-65: Robust free/paid checkCompare as numeric to handle '0', '0.0', or different formatting.
- <?php if ( $billing_amount != '0.00' ) { ?> + <?php if ( floatval( $billing_amount ) > 0 ) { ?>
67-73: Meta key mismatch for recurring flagCode mixes
recurring_payand_recurring_pay. Make the trial text condition resilient.-<?php if ( wpuf_is_checkbox_or_toggle_on( $pack->meta_value['recurring_pay'] ) && !empty( $trial_des ) ) { ?> +<?php +$has_recurring = ( + ( isset( $pack->meta_value['recurring_pay'] ) && wpuf_is_checkbox_or_toggle_on( $pack->meta_value['recurring_pay'] ) ) || + ( isset( $pack->meta_value['_recurring_pay'] ) && 'yes' === $pack->meta_value['_recurring_pay'] ) +); +if ( $has_recurring && ! empty( $trial_des ) ) { ?>To confirm keys used elsewhere in the codebase, search for where pack meta is populated and which key is authoritative.
319-347: Replace inline hover handlers with CSS classes and add a11y attributesUse CSS for hover, and wire up ARIA for toggles.
- <button + <button type="button" - class="wpuf-text-sm wpuf-font-medium wpuf-transition-colors wpuf-duration-200" + class="wpuf-text-sm wpuf-font-medium wpuf-transition-colors wpuf-duration-200 hover:wpuf-opacity-80" style="color: <?php echo esc_attr( $button_color ); ?>;" - onmouseover="this.style.opacity='0.8'" - onmouseout="this.style.opacity='1'" data-wpuf-toggle-features="true" data-wpuf-pack-id="<?php echo esc_attr( $pack->ID ); ?>" data-expanded="false" data-hidden-count="<?php echo esc_attr( $features_count - $initial_display_count ); ?>" id="wpuf-see-more-btn-<?php echo esc_attr( $pack->ID ); ?>" + aria-controls="wpuf-features-list-<?php echo esc_attr( $pack->ID ); ?>" + aria-expanded="false" > @@ - <button + <button type="button" - class="wpuf-text-sm wpuf-font-medium wpuf-transition-colors wpuf-duration-200 wpuf-hidden" + class="wpuf-text-sm wpuf-font-medium wpuf-transition-colors wpuf-duration-200 hover:wpuf-opacity-80 wpuf-hidden" style="color: <?php echo esc_attr( $button_color ); ?>;" - onmouseover="this.style.opacity='0.8'" - onmouseout="this.style.opacity='1'" data-wpuf-toggle-features="true" data-wpuf-pack-id="<?php echo esc_attr( $pack->ID ); ?>" data-expanded="true" data-hidden-count="<?php echo esc_attr( $features_count - $initial_display_count ); ?>" id="wpuf-see-less-btn-<?php echo esc_attr( $pack->ID ); ?>" + aria-controls="wpuf-features-list-<?php echo esc_attr( $pack->ID ); ?>" + aria-expanded="true" >
265-269: Fix pluralization and translate time unit labelsConcatenating “s” is not translatable. Use
_n()and translate the unit string.- if ( $expiry_period > 0 ) { - $features_list[] = sprintf( __( 'Posts expire after %d %s', 'wp-user-frontend' ), $expiry_period, $expiry_type . ($expiry_period > 1 ? 's' : '') ); - } else { + if ( $expiry_period > 0 ) { + $units = [ + 'day' => __( 'day', 'wp-user-frontend' ), + 'week' => __( 'week', 'wp-user-frontend' ), + 'month' => __( 'month', 'wp-user-frontend' ), + 'year' => __( 'year', 'wp-user-frontend' ), + ]; + $unit_label = isset( $units[ $expiry_type ] ) ? $units[ $expiry_type ] : $expiry_type; + $features_list[] = sprintf( + _n( 'Posts expire after %d %s', 'Posts expire after %d %s', $expiry_period, 'wp-user-frontend' ), + $expiry_period, + $expiry_period > 1 ? $unit_label . 's' : $unit_label + ); + } else { $features_list[] = __( 'Post expiration enabled', 'wp-user-frontend' ); } @@ - if ( $trial_duration > 0 ) { - $features_list[] = sprintf( __( '%d %s free trial', 'wp-user-frontend' ), $trial_duration, $trial_type . ($trial_duration > 1 ? 's' : '') ); - } else { + if ( $trial_duration > 0 ) { + $units = [ + 'day' => __( 'day', 'wp-user-frontend' ), + 'week' => __( 'week', 'wp-user-frontend' ), + 'month' => __( 'month', 'wp-user-frontend' ), + 'year' => __( 'year', 'wp-user-frontend' ), + ]; + $unit_label = isset( $units[ $trial_type ] ) ? $units[ $trial_type ] : $trial_type; + $features_list[] = sprintf( + _n( '%d %s free trial', '%d %s free trial', $trial_duration, 'wp-user-frontend' ), + $trial_duration, + $trial_duration > 1 ? $unit_label . 's' : $unit_label + ); + } else { $features_list[] = __( 'Free trial available', 'wp-user-frontend' ); }Also applies to: 282-286
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
 - Jira integration is disabled by default for public repositories
 - Linear integration is disabled by default for public repositories
 
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (6)
assets/js/forms-list.min.jsis excluded by!**/*.min.jsassets/js/forms-list.min.js.mapis excluded by!**/*.map,!**/*.min.js.mapassets/js/frontend-subscriptions.min.jsis excluded by!**/*.min.jsassets/js/subscriptions.min.jsis excluded by!**/*.min.jsassets/js/subscriptions.min.js.mapis excluded by!**/*.map,!**/*.min.js.mappackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (18)
assets/css/frontend-forms.css(1 hunks)assets/css/wpuf.css(0 hunks)assets/js/components/Subscriptions.vue(3 hunks)assets/js/components/subscriptions/Preferences.vue(1 hunks)assets/js/components/subscriptions/SidebarMenu.vue(1 hunks)assets/js/frontend-subscriptions.js(1 hunks)assets/js/stores/notice.js(1 hunks)assets/less/frontend-forms.less(2 hunks)includes/Api/Subscription.php(2 hunks)includes/Assets.php(2 hunks)includes/Frontend.php(2 hunks)package.json(1 hunks)src/css/admin/form-builder.css(2 hunks)src/css/frontend/subscriptions.css(1 hunks)tailwind.config.js(1 hunks)templates/subscriptions/listing.php(2 hunks)templates/subscriptions/pack-details.php(1 hunks)vite.config.mjs(1 hunks)
💤 Files with no reviewable changes (1)
- assets/css/wpuf.css
 
🧰 Additional context used
🧬 Code graph analysis (3)
includes/Frontend.php (1)
wpuf-functions.php (1)
wpuf_has_shortcode(1508-1530)
includes/Api/Subscription.php (1)
wpuf-functions.php (1)
wpuf_get_option(1489-1497)
templates/subscriptions/pack-details.php (1)
wpuf-functions.php (3)
wpuf_get_option(1489-1497)wpuf_format_price(2775-2805)wpuf_is_checkbox_or_toggle_on(5515-5517)
🪛 Biome (2.1.2)
src/css/frontend/subscriptions.css
[error] 82-82: expected } but instead the file ends
the file ends here
(parse)
🪛 PHPMD (2.15.0)
includes/Api/Subscription.php
559-559: Avoid unused parameters such as '$request'. (Unused Code Rules)
(UnusedFormalParameter)
🔇 Additional comments (15)
assets/less/frontend-forms.less (1)
793-953: LGTM: legacy pack styles disabled.Good containment via a single comment block so Tailwind can take precedence. Ensure the new Tailwind bundle is conditionally enqueued with the shortcode as intended.
assets/css/frontend-forms.css (1)
3-163: LGTM: legacy pack styles commented out in compiled CSS.No active selectors remain; reduces specificity conflicts with Tailwind. Verify no stray “@Mediasm” tokens leak since they are inside comments.
src/css/frontend/subscriptions.css (1)
74-82: Confirm Tailwind prefix/content config.Classes use the “wpuf-” prefix; ensure tailwind.config.js sets prefix: 'wpuf-' and includes all template globs so these classes aren’t purged.
tailwind.config.js (1)
13-18: Add hover shade and restore forms pluginmodule.exports = { theme: { extend: { colors: { - primary: '#059669', // emerald-600 + primary: '#059669', // emerald-600 + 'primary-700': '#047857' // emerald-700 }, }, }, - plugins: [], + plugins: [ + require('@tailwindcss/forms'), + ], }Verify that re-adding the
@tailwindcss/formsplugin doesn’t regress input, select, or checkbox styling and that you usehover:bg-primary-700(or equivalent) where needed instead of hardcoded emerald classes.assets/js/frontend-subscriptions.js (1)
1-2: Enqueue extracted CSS in WP for production
Register awpuf-frontend-subscriptionsstyle in your PHP assets loader (e.g. Assets.php) and callwp_enqueue_style('wpuf-frontend-subscriptions')alongside the script when the shortcode renders, since Vite’s CSS extraction won’t auto-load it.includes/Frontend.php (1)
39-76: Registration confirmed for new handles
wpuf-frontend-subscriptionsscript and style are registered in includes/Assets.php ($scripts['frontend-subscriptions']and$styles['frontend-subscriptions']), so the enqueue/localize calls are safe.assets/js/components/subscriptions/SidebarMenu.vue (1)
21-22: Settings item addition looks fine; ensure store/router support ‘preferences’Confirm:
- Router handles the ‘preferences’ key.
 subscriptionStore.allCount.preferenceswon’t break the badge (defaults to 0).If
allCount.preferencesis undefined, it’s safe but consider defaulting to 0 for clarity (see next comment).package.json (1)
17-17: Typography plugin addition looks fine.No issues with @tailwindcss/typography addition; ensure it’s enabled in tailwind.config.js content/plugins.
assets/js/components/Subscriptions.vue (2)
14-14: Importing Preferences component: LGTM.
103-105: Watcher addition for Preferences: LGTM.includes/Assets.php (2)
112-114: Registering frontend-subscriptions CSS: LGTM.Path and handle naming align with the Vite output pattern.
398-401: The snippet inFrontend.phpshows the subscription script enqueue is toggled exclusively based on the shortcode presence—no double-loading of old/new bundles whenwpuf_sub_packis present. The build artifacts referenced inAssets.php(frontend-subscriptions.min.js/.css) exist and match the handles. No further action needed.templates/subscriptions/listing.php (1)
29-29: Updated UL layout classes: LGTM.vite.config.mjs (2)
4-7: New multi-entry (frontend-subscriptions) wiring: LGTM.
22-26: Only admin CSS asset ‘subscriptions.css’ is routed to css/admin/
Verified that the admin entrypoint (assets/js/subscriptions.js) imports../css/admin/subscriptions.css, producing an asset namedsubscriptions.css, while the frontend entry (frontend-subscriptions.js) imports its CSS and outputsfrontend-subscriptions.css, which won’t match this rule. No changes needed.
| const saveSettings = async () => { | ||
| isSaving.value = true; | ||
| try { | ||
| const response = await fetch(`${wpufSubscriptions.rest_url}wpuf/v1/subscription-settings`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'X-WP-Nonce': wpufSubscriptions.nonce | ||
| }, | ||
| body: JSON.stringify(settings.value) | ||
| }); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Validate color before saving to avoid persisting invalid values
Guard against malformed input (e.g., non-hex strings) and provide immediate user feedback.
 const saveSettings = async () => {
-    isSaving.value = true;
+    // Basic hex color validation (#RRGGBB)
+    if (!/^#[0-9a-fA-F]{6}$/.test(settings.value.button_color)) {
+        noticeStore.addNotice({
+            type: 'error',
+            message: __('Please enter a valid 6‑digit hex color (e.g., #4f46e5)', 'wp-user-frontend')
+        });
+        return;
+    }
+    isSaving.value = true;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const saveSettings = async () => { | |
| isSaving.value = true; | |
| try { | |
| const response = await fetch(`${wpufSubscriptions.rest_url}wpuf/v1/subscription-settings`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'X-WP-Nonce': wpufSubscriptions.nonce | |
| }, | |
| body: JSON.stringify(settings.value) | |
| }); | |
| const saveSettings = async () => { | |
| // Basic hex color validation (#RRGGBB) | |
| if (!/^#[0-9a-fA-F]{6}$/.test(settings.value.button_color)) { | |
| noticeStore.addNotice({ | |
| type: 'error', | |
| message: __('Please enter a valid 6-digit hex color (e.g., #4f46e5)', 'wp-user-frontend') | |
| }); | |
| return; | |
| } | |
| isSaving.value = true; | |
| try { | |
| const response = await fetch( | |
| `${wpufSubscriptions.rest_url}wpuf/v1/subscription-settings`, | |
| { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'X-WP-Nonce': wpufSubscriptions.nonce | |
| }, | |
| body: JSON.stringify(settings.value) | |
| } | |
| ); | |
| // …rest of save logic… | 
| href="<?php echo esc_attr( add_query_arg( $query_args, esc_url( $query_url ) ) ); ?>" | ||
| onclick="<?php echo esc_attr( $details_meta['onclick'] ); ?>"> | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix URL escaping for add_query_arg
Avoid passing an escaped URL into add_query_arg and prefer esc_url for hrefs.
-               href="<?php echo esc_attr( add_query_arg( $query_args, esc_url( $query_url ) ) ); ?>" 
+               href="<?php echo esc_url( add_query_arg( $query_args, $query_url ) ); ?>" 📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| href="<?php echo esc_attr( add_query_arg( $query_args, esc_url( $query_url ) ) ); ?>" | |
| onclick="<?php echo esc_attr( $details_meta['onclick'] ); ?>"> | |
| href="<?php echo esc_url( add_query_arg( $query_args, $query_url ) ); ?>" | |
| onclick="<?php echo esc_attr( $details_meta['onclick'] ); ?>"> | 
🤖 Prompt for AI Agents
In templates/subscriptions/pack-details.php around lines 92-93, avoid passing an
already escaped URL into add_query_arg; instead call add_query_arg with the raw
$query_url and then escape the result for output. Replace the current sequence
so the href uses esc_url( add_query_arg( $query_args, $query_url ) ) and keep
onclick using esc_attr( $details_meta['onclick'] ), ensuring add_query_arg
receives an unescaped URL and the final href is properly escaped for output.
| 
           Please resolve merge conflicts at your convenience @arifulhoque7 vai and let me know after completion  | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️  Outside diff range comments (1)
assets/less/frontend-forms.less (1)
799-959: Gate legacy subscription-pack styles instead of blanket-commenting
Move the.wpuf_packsrules into a dedicated “legacy” stylesheet (e.g.frontend-subscriptions-legacy.css) and enqueue it only when legacy templates are rendered or a feature flag/filter (e.g.apply_filters('wpuf_enable_legacy_pack_styles', false)) is active. Alternatively, scope all selectors under a.wpuf-legacyroot class and apply that class in old templates.
Include a deprecation notice and removal timeline.References to update:
- templates/subscriptions/listing.php (inline
 .wpuf_packsmarkup)- src/css/frontend/subscriptions.css (lines ~69–71)
 - assets/css/frontend-forms.css (lines ~4–7)
 
🧹 Nitpick comments (1)
assets/less/frontend-forms.less (1)
799-799: Make comment markers explicit and versionedUse clear BEGIN/END markers and note deprecation to aid future cleanup.
-/* Commented out to allow Tailwind styles for subscription packs +/* BEGIN: Legacy wpuf_packs styles (disabled to allow Tailwind subscription UI). + * Deprecated: add version here (e.g., since 4.0). Planned removal: add version/date. + */ -End of commented pack styles */ +/* END: Legacy wpuf_packs styles */Also applies to: 959-959
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
 - Jira integration is disabled by default for public repositories
 - Linear integration is disabled by default for public repositories
 
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (4)
assets/js/forms-list.min.js.mapis excluded by!**/*.map,!**/*.min.js.mapassets/js/subscriptions.min.jsis excluded by!**/*.min.jsassets/js/subscriptions.min.js.mapis excluded by!**/*.map,!**/*.min.js.mappackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (2)
assets/css/frontend-forms.css(1 hunks)assets/less/frontend-forms.less(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- assets/css/frontend-forms.css
 
- Moved JavaScript toggle function from pack-details.php to listing.php - Function is now defined only once instead of being redefined for each pack - Added unique IDs for each pack's elements to ensure proper isolation - Added null checks to prevent errors if elements are not found - Each subscription pack card now expands/collapses independently This fixes the issue where clicking "See more" on one subscription pack would expand all packs simultaneously. Now each pack's features list expands and collapses independently as expected. 🤖 Generated with Claude Code Co-Authored-By: Claude <[email protected]>
- Replaced onclick handlers with data attributes and event delegation - Added unique data-wpuf-pack-id attributes for proper isolation - Implemented event listener on document level to handle all toggles - Added data-expanded attribute to track button state - Prevents multiple event listener registrations with initialization flag - Each pack now properly expands/collapses independently This ensures that clicking "See more" on one subscription pack only affects that specific pack, not others on the page. 🤖 Generated with Claude Code Co-Authored-By: Claude <[email protected]>
24f96c5    to
    8a0eda9      
    Compare
  
    | 
           done @Rubaiyat-E-Mohammad vai . please check now  | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (9)
assets/js/stores/notice.js (2)
11-15: Prevent accidental removal of the last notice when the notice is already goneCompute the index at timeout and guard against -1 before removing.
Apply:
- setTimeout(() => { - this.removeNotice(this.notices.indexOf(notice)); - }, 3000); + setTimeout(() => { + const i = this.notices.indexOf(notice); + if ( i !== -1 ) { + this.removeNotice(i); + } + }, 3000);
17-22: Harden removeNotice against invalid indicesAvoid splicing with -1/out-of-bounds.
Apply:
- removeNotice( index ) { - this.notices.splice( index, 1 ); - if (this.notices.length === 0) { - this.display = false; - } - }, + removeNotice( index ) { + if ( !Number.isInteger(index) || index < 0 || index >= this.notices.length ) { + return; + } + this.notices.splice( index, 1 ); + if ( this.notices.length === 0 ) { + this.display = false; + } + },includes/Api/Subscription.php (1)
576-592: Validate hex color and avoid storing nulls; return 400 on bad inputsanitize_hex_color can return null. Reject invalid values and store only valid ones.
- public function update_subscription_settings( $request ) { - $params = $request->get_params(); + public function update_subscription_settings( $_request ) { + $params = $_request->get_params(); @@ - if ( isset( $params['button_color'] ) ) { - $settings['button_color'] = sanitize_hex_color( $params['button_color'] ); - } + if ( isset( $params['button_color'] ) ) { + $color = sanitize_hex_color( $params['button_color'] ); + if ( empty( $color ) ) { + return new WP_REST_Response( + [ + 'success' => false, + 'message' => __( 'Invalid button color. Use a valid hex value (e.g., #4f46e5).', 'wp-user-frontend' ), + ], + 400 + ); + } + $settings['button_color'] = $color; + }src/css/frontend/subscriptions.css (1)
7-9: Missing semicolon after @apply breaks the buildAdd the semicolon.
.subscription-template-classes { - @apply wpuf-rounded-xl wpuf-p-6 wpuf-ring-1 wpuf-ring-gray-200 wpuf-bg-white wpuf-shadow-md hover:wpuf-shadow-lg wpuf-transition-all wpuf-duration-300 wpuf-relative wpuf-h-full wpuf-flex wpuf-flex-col wpuf-m-2 + @apply wpuf-rounded-xl wpuf-p-6 wpuf-ring-1 wpuf-ring-gray-200 wpuf-bg-white wpuf-shadow-md hover:wpuf-shadow-lg wpuf-transition-all wpuf-duration-300 wpuf-relative wpuf-h-full wpuf-flex wpuf-flex-col wpuf-m-2; }This should also resolve the parser “expected }” error.
includes/Frontend.php (1)
69-76: Enqueue the stylesheet too to prevent FOUC/missing stylesLoad CSS alongside the new script; keep legacy CSS when falling back.
- // Load appropriate subscription script based on shortcode - if ( wpuf_has_shortcode( 'wpuf_sub_pack' ) ) { - // Load new frontend-subscriptions script for subscription pack shortcode (pricing page) - wp_enqueue_script( 'wpuf-frontend-subscriptions' ); - } else { - // Load old subscriptions script for all other pages (dashboard, account, etc.) - wp_enqueue_script( 'wpuf-subscriptions' ); - } + // Load appropriate subscription assets based on shortcode + $has_sub_pack_shortcode = wpuf_has_shortcode( 'wpuf_sub_pack' ); + if ( $has_sub_pack_shortcode ) { + // New pricing page assets + wp_enqueue_style( 'wpuf-frontend-subscriptions' ); + wp_enqueue_script( 'wpuf-frontend-subscriptions' ); + } else { + // Legacy assets (dashboard, account, etc.) + if ( wp_style_is( 'wpuf-subscriptions', 'registered' ) ) { + wp_enqueue_style( 'wpuf-subscriptions' ); + } + wp_enqueue_script( 'wpuf-subscriptions' ); + }templates/subscriptions/pack-details.php (4)
17-19: Sanitize theme color before injecting into inline CSSConstrain to a valid hex; fall back to default.
-// Get the button color from settings -$button_color = wpuf_get_option( 'button_color', 'wpuf_subscription_settings', '#4f46e5' ); +// Get and sanitize button color from settings +$raw_color = wpuf_get_option( 'button_color', 'wpuf_subscription_settings', '#4f46e5' ); +$button_color = sanitize_hex_color( $raw_color ); +if ( empty( $button_color ) ) { + $button_color = '#4f46e5'; +}
77-84: Don’t escape for comparison; render a truly disabled controlCompare raw value, and remove onclick so it can’t trigger actions.
- <?php if ( esc_attr( $current_pack_status ) == 'completed' ) : ?> - <a class="wpuf-block wpuf-w-full wpuf-rounded-md wpuf-px-3 wpuf-py-2 wpuf-text-center wpuf-text-sm wpuf-font-semibold wpuf-text-gray-400 wpuf-ring-1 wpuf-ring-inset wpuf-ring-gray-300 wpuf-cursor-not-allowed wpuf-bg-gray-100 wpuf-leading-6" - href="javascript:void(0)" - onclick="<?php echo esc_attr( $details_meta['onclick'] ); ?>"> + <?php if ( 'completed' === (string) $current_pack_status ) : ?> + <a class="wpuf-block wpuf-w-full wpuf-rounded-md wpuf-px-3 wpuf-py-2 wpuf-text-center wpuf-text-sm wpuf-font-semibold wpuf-text-gray-400 wpuf-ring-1 wpuf-ring-inset wpuf-ring-gray-300 wpuf-cursor-not-allowed wpuf-bg-gray-100 wpuf-leading-6" + href="#" + role="button" + aria-disabled="true" + tabindex="-1"> <?php echo esc_html( $button_name ); ?> </a>
85-95: Fix URL escaping order; avoid passing an escaped URL to add_query_argEscape the final href only.
- <a class="wpuf-subscription-buy-btn wpuf-block wpuf-w-full wpuf-rounded-md wpuf-px-3 wpuf-py-2 wpuf-text-center wpuf-text-sm wpuf-font-semibold wpuf-text-white wpuf-shadow-sm wpuf-ring-0 wpuf-transition-all wpuf-duration-200 wpuf-leading-6" - style="background-color: <?php echo esc_attr( $button_color ); ?>;" - href="<?php echo esc_attr( add_query_arg( $query_args, esc_url( $query_url ) ) ); ?>" - onclick="<?php echo esc_attr( $details_meta['onclick'] ); ?>"> + <a class="wpuf-subscription-buy-btn wpuf-block wpuf-w-full wpuf-rounded-md wpuf-px-3 wpuf-py-2 wpuf-text-center wpuf-text-sm wpuf-font-semibold wpuf-text-white wpuf-shadow-sm wpuf-ring-0 wpuf-transition-all wpuf-duration-200 wpuf-leading-6" + style="background-color: <?php echo esc_attr( $button_color ); ?>;" + href="<?php echo esc_url( add_query_arg( $query_args, $query_url ) ); ?>" + onclick="<?php echo esc_attr( $details_meta['onclick'] ); ?>">
355-360: Gate early and return before rendering anythingMove the action/coupon check to the top to avoid output before bail-out.
-<?php -$action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : ''; - -if ( $action == 'wpuf_pay' || $coupon_status ) { - return; -} -?> +<?php +// Gate before any output +$action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : ''; +if ( 'wpuf_pay' === $action || ! empty( $coupon_status ) ) { + return; +} +?>Place this block right after the file header docblock and before computing the color.
🧹 Nitpick comments (7)
includes/Api/Subscription.php (2)
550-566: Silence unused parameter warning in get_subscription_settingsRename the unused parameter to $_request to satisfy linters without changing the signature.
- * @param WP_REST_Request $request + * @param WP_REST_Request $_request @@ - public function get_subscription_settings( $request ) { + public function get_subscription_settings( $_request ) {
576-592: Avoid wiping future settings; merge instead of replacingReplacing the entire wpuf_subscription_settings option with a sparse array loses unknown keys. Consider merging with existing to be forward-compatible.
- // Only keep button_color, remove old settings - $settings = []; + // Only keep known keys; merge with existing to avoid wiping future settings + $existing = (array) get_option( 'wpuf_subscription_settings', [] ); + $settings = []; @@ - update_option( 'wpuf_subscription_settings', $settings ); + update_option( 'wpuf_subscription_settings', array_merge( $existing, $settings ) );src/css/frontend/subscriptions.css (1)
68-71: Avoid global !important override; prefer utility on the containerThe hard rule can cause unintended spacing elsewhere. Add spacing on the wrapper element (e.g., via wpuf-mt-8) instead.
includes/Frontend.php (1)
162-166: Reuse the shortcode check result to avoid duplicate callsUse the boolean from above.
- $subscription_script_handle = wpuf_has_shortcode( 'wpuf_sub_pack' ) ? 'wpuf-frontend-subscriptions' : 'wpuf-subscriptions'; + $subscription_script_handle = ! empty( $has_sub_pack_shortcode ) + ? 'wpuf-frontend-subscriptions' + : 'wpuf-subscriptions';templates/subscriptions/pack-details.php (3)
21-30: Inline CSS is fine for FOUC; consider moving to the new CSS module laterNot blocking; keep critical styles inline now, migrate to src/css/frontend/subscriptions.css when safe.
50-66: Avoid string comparison for pricesCompare numerically to handle values like '0', '0.0', 0.
- <?php if ( $billing_amount != '0.00' ) { ?> + <?php if ( floatval( $billing_amount ) > 0 ) { ?>
260-270: Use proper pluralization for i18n (_n) instead of manual “s”Ensure correct translations for singular/plural.
- $features_list[] = sprintf( __( 'Posts expire after %d %s', 'wp-user-frontend' ), $expiry_period, $expiry_type . ($expiry_period > 1 ? 's' : '') ); + $features_list[] = sprintf( + /* translators: 1: number, 2: period label (translated) */ + __( 'Posts expire after %1$d %2$s', 'wp-user-frontend' ), + $expiry_period, + _n( $expiry_type, $expiry_type . 's', $expiry_period, 'wp-user-frontend' ) + ); @@ - $features_list[] = sprintf( __( '%d %s free trial', 'wp-user-frontend' ), $trial_duration, $trial_type . ($trial_duration > 1 ? 's' : '') ); + $features_list[] = sprintf( + __( '%1$d %2$s free trial', 'wp-user-frontend' ), + $trial_duration, + _n( $trial_type, $trial_type . 's', $trial_duration, 'wp-user-frontend' ) + );If period labels are not English nouns (e.g., “month”), consider mapping to translatable labels first.
Also applies to: 282-286
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
 - Jira integration is disabled by default for public repositories
 - Linear integration is disabled by default for public repositories
 
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (6)
assets/js/forms-list.min.jsis excluded by!**/*.min.jsassets/js/forms-list.min.js.mapis excluded by!**/*.map,!**/*.min.js.mapassets/js/frontend-subscriptions.min.jsis excluded by!**/*.min.jsassets/js/subscriptions.min.jsis excluded by!**/*.min.jsassets/js/subscriptions.min.js.mapis excluded by!**/*.map,!**/*.min.js.mappackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (18)
assets/css/frontend-forms.css(1 hunks)assets/css/wpuf.css(0 hunks)assets/js/components/Subscriptions.vue(3 hunks)assets/js/components/subscriptions/Preferences.vue(1 hunks)assets/js/components/subscriptions/SidebarMenu.vue(1 hunks)assets/js/frontend-subscriptions.js(1 hunks)assets/js/stores/notice.js(1 hunks)assets/less/frontend-forms.less(2 hunks)includes/Api/Subscription.php(2 hunks)includes/Assets.php(2 hunks)includes/Frontend.php(2 hunks)package.json(1 hunks)src/css/admin/form-builder.css(2 hunks)src/css/frontend/subscriptions.css(1 hunks)tailwind.config.js(1 hunks)templates/subscriptions/listing.php(2 hunks)templates/subscriptions/pack-details.php(1 hunks)vite.config.mjs(1 hunks)
💤 Files with no reviewable changes (1)
- assets/css/wpuf.css
 
🚧 Files skipped from review as they are similar to previous changes (12)
- src/css/admin/form-builder.css
 - assets/js/frontend-subscriptions.js
 - assets/css/frontend-forms.css
 - templates/subscriptions/listing.php
 - assets/js/components/subscriptions/Preferences.vue
 - vite.config.mjs
 - includes/Assets.php
 - package.json
 - assets/js/components/subscriptions/SidebarMenu.vue
 - tailwind.config.js
 - assets/less/frontend-forms.less
 - assets/js/components/Subscriptions.vue
 
🧰 Additional context used
🧬 Code graph analysis (3)
includes/Frontend.php (1)
wpuf-functions.php (1)
wpuf_has_shortcode(1554-1576)
templates/subscriptions/pack-details.php (1)
wpuf-functions.php (3)
wpuf_get_option(1535-1543)wpuf_format_price(2870-2900)wpuf_is_checkbox_or_toggle_on(5592-5594)
includes/Api/Subscription.php (1)
wpuf-functions.php (1)
wpuf_get_option(1535-1543)
🪛 PHPMD (2.15.0)
includes/Api/Subscription.php
559-559: Avoid unused parameters such as '$request'. (Unused Code Rules)
(UnusedFormalParameter)
🪛 Biome (2.1.2)
src/css/frontend/subscriptions.css
[error] 82-82: expected } but instead the file ends
the file ends here
(parse)
🔇 Additional comments (4)
includes/Api/Subscription.php (2)
100-116: REST route registration for settings looks good
1-20: Scope check: linked issue #993 (product category in emails) seems unaddressedThis PR adds UI/settings for subscriptions, but I don’t see changes that fix product_cat missing in notifications/logs. Confirm scope or point me to the file handling placeholders/email rendering.
templates/subscriptions/pack-details.php (2)
31-42: Header section: clear and accessibleGood use of semantic tags and Tailwind classes; featured badge gated by isset.
67-73: Trial display logic looks correctGated by recurring flag and non-empty description.
          
  
       | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
assets/js/components/subscriptions/Preferences.vue (1)
36-39: Validate hex color before saving (prevents bad data).Add a basic
#RRGGBBcheck and normalize before POST. This was flagged earlier and is still unresolved.-const saveSettings = async () => { - isSaving.value = true; +const saveSettings = async () => { + // Validate hex color (#RRGGBB) + const color = (settings.value.button_color || '').trim(); + if (!/^#[0-9a-fA-F]{6}$/.test(color)) { + noticeStore.addNotice({ + type: 'error', + message: __('Please enter a valid 6‑digit hex color (e.g., #4f46e5)', 'wp-user-frontend') + }); + return; + } + settings.value.button_color = color.toLowerCase(); + isSaving.value = true;
🧹 Nitpick comments (7)
assets/js/components/subscriptions/Preferences.vue (7)
117-118: Avoid inline DOM event handlers; use classes instead (CSP/SSR-friendly).- class="wpuf-subscription-buy-btn wpuf-block wpuf-w-full wpuf-rounded-md wpuf-px-3 wpuf-py-2 wpuf-text-center wpuf-text-sm wpuf-font-semibold wpuf-text-white wpuf-shadow-sm wpuf-ring-0 wpuf-transition-all wpuf-duration-200 wpuf-leading-6" - onmouseover="this.style.filter='brightness(0.9)'" - onmouseout="this.style.filter='brightness(1)'"> + class="wpuf-subscription-buy-btn wpuf-block wpuf-w-full wpuf-rounded-md wpuf-px-3 wpuf-py-2 wpuf-text-center wpuf-text-sm wpuf-font-semibold wpuf-text-white wpuf-shadow-sm wpuf-ring-0 wpuf-transition-all wpuf-duration-200 wpuf-leading-6 wpuf-filter hover:wpuf-brightness-90">
2-2: Cancel in-flight loads on unmount or rapid route changes.-import { ref, onMounted } from 'vue'; +import { ref, onMounted, onUnmounted } from 'vue'; @@ const settings = ref({ button_color: '#4f46e5' }); +let loadController; @@ const loadSettings = async () => { - try { - const response = await fetch(`${wpufSubscriptions.rest_url}wpuf/v1/subscription-settings`, { - headers: { - 'X-WP-Nonce': wpufSubscriptions.nonce - } - }); + // Cancel any in‑flight request + loadController?.abort(); + loadController = new AbortController(); + try { + const response = await fetch(`${wpufSubscriptions.rest_url}wpuf/v1/subscription-settings`, { + signal: loadController.signal, + headers: { + 'X-WP-Nonce': wpufSubscriptions.nonce + } + }); @@ - } catch (error) { - console.error('Error loading settings:', error); - } + } catch (error) { + if (error?.name !== 'AbortError') { + noticeStore.addNotice({ + type: 'error', + message: __('Failed to load preferences', 'wp-user-frontend') + }); + } + } +}; + +onUnmounted(() => { + loadController?.abort(); }); -};Also applies to: 19-26, 31-34
49-61: Surface WP REST error messages to users.- if (response.ok) { + if (response.ok) { noticeStore.addNotice({ type: 'success', message: __('Preferences saved successfully', 'wp-user-frontend') }); - } else { - throw new Error('Failed to save settings'); + } else { + let msg = __('Failed to save settings', 'wp-user-frontend'); + try { + const err = await response.json(); + if (err?.message) msg = err.message; + } catch (_) {} + throw new Error(msg); } } catch (error) { noticeStore.addNotice({ type: 'error', - message: __('Failed to save settings', 'wp-user-frontend') + message: error?.message || __('Failed to save settings', 'wp-user-frontend') });
89-111: A11y: associate label, add help text, and native validation hints.- <label class="wpuf-text-sm wpuf-font-medium wpuf-text-gray-700"> + <label for="primary-color" class="wpuf-text-sm wpuf-font-medium wpuf-text-gray-700"> {{ __('Primary Color', 'wp-user-frontend') }} </label> @@ - </div> + </div> + <p id="primary-color-help" class="wpuf-sr-only"> + {{ __('Enter a 6‑digit hex color like #4f46e5', 'wp-user-frontend') }} + </p> <div class="wpuf-flex wpuf-items-center wpuf-space-x-3"> <input + id="primary-color" + name="primary-color" + aria-describedby="primary-color-help" v-model="settings.button_color" type="color" class="wpuf-h-10 wpuf-w-20 wpuf-rounded wpuf-border wpuf-border-gray-300 wpuf-cursor-pointer" /> <input + id="primary-color-text" + name="primary-color-text" + aria-describedby="primary-color-help" v-model="settings.button_color" type="text" + placeholder="#4f46e5" + pattern="^#[0-9a-fA-F]{6}$" + title="#RRGGBB" class="wpuf-rounded-md wpuf-border-gray-300 wpuf-shadow-sm focus:wpuf-border-primary focus:wpuf-ring-primary wpuf-text-sm wpuf-w-32" />
2-2: Auto-contrast preview text for accessibility.-import { ref, onMounted, onUnmounted } from 'vue'; +import { ref, onMounted, onUnmounted, computed } from 'vue'; @@ const settings = ref({ button_color: '#4f46e5' }); +function hexToRgb(hex) { + const m = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex || ''); + if (!m) return null; + return { r: parseInt(m[1], 16), g: parseInt(m[2], 16), b: parseInt(m[3], 16) }; +} + +function getContrastColor(hex) { + const rgb = hexToRgb(hex); + if (!rgb) return '#ffffff'; + const [r, g, b] = [rgb.r, rgb.g, rgb.b].map(c => { + c /= 255; + return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4); + }); + const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b; + return luminance > 0.5 ? '#111827' : '#ffffff'; +} + +const previewTextColor = computed(() => getContrastColor(settings.value.button_color)); @@ - :style="{ backgroundColor: settings.button_color }" - class="wpuf-subscription-buy-btn wpuf-block wpuf-w-full wpuf-rounded-md wpuf-px-3 wpuf-py-2 wpuf-text-center wpuf-text-sm wpuf-font-semibold wpuf-text-white wpuf-shadow-sm wpuf-ring-0 wpuf-transition-all wpuf-duration-200 wpuf-leading-6 wpuf-filter hover:wpuf-brightness-90"> + :style="{ backgroundColor: settings.button_color, color: previewTextColor }" + class="wpuf-subscription-buy-btn wpuf-block wpuf-w-full wpuf-rounded-md wpuf-px-3 wpuf-py-2 wpuf-text-center wpuf-text-sm wpuf-font-semibold wpuf-shadow-sm wpuf-ring-0 wpuf-transition-all wpuf-duration-200 wpuf-leading-6 wpuf-filter hover:wpuf-brightness-90">Also applies to: 10-16, 113-116
15-18: Guard against missing globals to avoid runtime errors.onMounted(() => { - loadSettings(); + if (!window?.wpufSubscriptions?.rest_url || !window?.wpufSubscriptions?.nonce) { + noticeStore.addNotice({ type: 'error', message: __('Subscriptions script not initialized', 'wp-user-frontend') }); + return; + } + loadSettings(); });
21-25: Optional: use@wordpress/api-fetchfor nonce/root handling and middleware.If feasible, replace raw
fetchwithapiFetch:import apiFetch from '@wordpress/api-fetch'; // GET const data = await apiFetch({ path: '/wpuf/v1/subscription-settings', method: 'GET' }); // POST await apiFetch({ path: '/wpuf/v1/subscription-settings', method: 'POST', data: settings.value });This centralizes auth, errors, and interceptors.
Also applies to: 40-47
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
 - Jira integration is disabled by default for public repositories
 - Linear integration is disabled by default for public repositories
 
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
assets/js/components/subscriptions/Preferences.vue(1 hunks)assets/js/components/subscriptions/SidebarMenu.vue(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- assets/js/components/subscriptions/SidebarMenu.vue
 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please check the comments
| ); | ||
| 
               | 
          ||
| // Register subscription settings endpoints | ||
| register_rest_route( | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can use existing API endpoints. please recheck
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The /subscription-settings REST API endpoint is not redundant. It serves a distinct purpose from the main /wpuf_subscription API. While /wpuf_subscription is responsible for managing subscription package data (CRUD operations, billing, expiration, features, etc.), the /subscription-settings endpoint manages global subscription appearance preferences, such as UI settings (button_color).
This endpoint is currently used by the Preferences.vue component to configure and persist display settings applied across all subscription packages. Removing it would break existing functionality and mix unrelated concerns into the subscription package API.
Conclusion:
Keeping /subscription-settings maintains a clear separation of concerns:
/wpuf_subscription → Subscription package data
/subscription-settings → Global subscription UI/display settings
This separation improves maintainability and prevents coupling between data management and UI configuration.
| 
               | 
          ||
| @layer components { | ||
| .wpuf-btn-primary { | ||
| @apply wpuf-rounded-md wpuf-bg-primary !wpuf-text-white wpuf-px-4 wpuf-py-3 wpuf-text-sm wpuf-font-semibold hover:wpuf-bg-emerald-700 hover:wpuf-text-white focus:wpuf-text-white focus:wpuf-shadow-none; | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use wpuf-bg-primary. this is dynamically setting from tailwindcss config
        
          
                package.json
              
                Outdated
          
        
      | }, | ||
| "devDependencies": { | ||
| "@tailwindcss/forms": "^0.5.7", | ||
| "@tailwindcss/typography": "^0.5.16", | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need this package?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed @sapayth vai
| 
           done @sapayth vai  | 
    
01bd118    to
    a36eed4      
    Compare
  
    There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (9)
includes/Api/Subscription.php (1)
559-566: Minor: Unused parameter in GET handler.Avoid PHPMD/PHPCS noise by marking or renaming the unused param.
- public function get_subscription_settings( $request ) { + public function get_subscription_settings( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariableAlternatively rename to
$_request.assets/js/components/Subscriptions.vue (2)
51-63: Don’t set list-status to 'preferences'; keep navigation separate.Setting
currentSubscriptionStatus = 'preferences'can confuse list loaders/watchers. Use component-only navigation.if (subscriptionStatus === 'preferences') { // Handle preferences separately if (subscriptionStore.isDirty) { subscriptionStore.isUnsavedPopupOpen = true; tempNavigationTarget.value = 'preferences'; // Keep current subscription status for list navigation tempSubscriptionStatus.value = subscriptionStore.currentSubscriptionStatus || 'all'; } else { componentStore.setCurrentComponent( 'Preferences' ); - subscriptionStore.currentSubscriptionStatus = 'preferences'; } } else if (subscriptionStore.isDirty) {
83-93: Same here: avoid mutating list-status when opening Preferences.// Check if we're navigating to preferences if (tempNavigationTarget.value === 'preferences') { componentStore.setCurrentComponent( 'Preferences' ); - subscriptionStore.currentSubscriptionStatus = 'preferences'; tempNavigationTarget.value = null; } else {templates/subscriptions/listing.php (2)
16-26: Inline critical CSS: consider moving to the subscription CSS to reduce duplication.Keep the FOUC fix but prefer central CSS if possible.
54-119: Event delegation + per-pack scoping looks solid; nice a11y touches.Using closest(), per-pack IDs, and updating aria-expanded is correct.
Optionally add
aria-controls="wpuf-features-list-{packId}"to the toggles.assets/js/components/subscriptions/Preferences.vue (1)
139-141: Focus styles reference primary; ensure ring colors exist.After defining
primary,focus-visible:wpuf-outline-primarystill won’t generate. Prefer ring utilities:- class="wpuf-rounded-md wpuf-bg-primary wpuf-px-4 wpuf-py-2 wpuf-text-sm wpuf-font-semibold wpuf-text-white wpuf-shadow-sm hover:wpuf-bg-primaryHover focus-visible:wpuf-outline focus-visible:wpuf-outline-2 focus-visible:wpuf-outline-offset-2 focus-visible:wpuf-outline-primary disabled:wpuf-opacity-50"> + class="wpuf-rounded-md wpuf-bg-primary wpuf-px-4 wpuf-py-2 wpuf-text-sm wpuf-font-semibold wpuf-text-white wpuf-shadow-sm hover:wpuf-bg-primaryHover focus-visible:wpuf-ring-2 focus-visible:wpuf-ring-primary focus-visible:wpuf-ring-offset-2 disabled:wpuf-opacity-50">templates/subscriptions/pack-details.php (3)
84-91: Likely wrong meta key: use '_recurring_pay' for consistency.Else the trial message may not show.
- <?php if ( wpuf_is_checkbox_or_toggle_on( $pack->meta_value['recurring_pay'] ) && !empty( $trial_des ) ) { ?> + <?php if ( wpuf_is_checkbox_or_toggle_on( $pack->meta_value['_recurring_pay'] ?? '' ) && ! empty( $trial_des ) ) { ?>
95-100: Prefer semantic disabled link markup.Avoid
javascript:void(0), add a11y hints.- <a class="wpuf-block wpuf-w-full wpuf-rounded-md wpuf-px-3 wpuf-py-2 wpuf-text-center wpuf-text-sm wpuf-font-semibold wpuf-text-gray-400 wpuf-ring-1 wpuf-ring-inset wpuf-ring-gray-300 wpuf-cursor-not-allowed wpuf-bg-gray-100 wpuf-leading-6" - href="javascript:void(0)"> + <a class="wpuf-block wpuf-w-full wpuf-rounded-md wpuf-px-3 wpuf-py-2 wpuf-text-center wpuf-text-sm wpuf-font-semibold wpuf-text-gray-400 wpuf-ring-1 wpuf-ring-inset wpuf-ring-gray-300 wpuf-cursor-not-allowed wpuf-bg-gray-100 wpuf-leading-6" + href="#" + role="button" + aria-disabled="true" + tabindex="-1">
329-333: Avoid empty fill attr; use currentColor with class fallback.If no custom color,
fill=""is invalid and defaults unpredictably. Tie icon color to text via currentColor and primary token.- <svg viewBox="0 0 20 20" fill="<?php echo esc_attr( $button_color ); ?>" class="wpuf-h-6 wpuf-w-5 wpuf-flex-none" style="width: 1.25rem; height: 1.5rem; flex-shrink: 0;" width="20" height="24" aria-hidden="true"> + <?php if ( $use_custom_color ) : ?> + <svg viewBox="0 0 20 20" fill="<?php echo esc_attr( $button_color ); ?>" class="wpuf-h-6 wpuf-w-5 wpuf-flex-none" style="width: 1.25rem; height: 1.5rem; flex-shrink: 0;" width="20" height="24" aria-hidden="true"> + <?php else : ?> + <svg viewBox="0 0 20 20" fill="currentColor" class="wpuf-h-6 wpuf-w-5 wpuf-flex-none wpuf-text-primary" style="width: 1.25rem; height: 1.5rem; flex-shrink: 0;" width="20" height="24" aria-hidden="true"> + <?php endif; ?>Note: relies on defining
primaryin Tailwind.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
assets/js/frontend-subscriptions.min.jsis excluded by!**/*.min.js
📒 Files selected for processing (8)
assets/js/components/Subscriptions.vue(6 hunks)assets/js/components/subscriptions/Preferences.vue(1 hunks)assets/js/stores/notice.js(1 hunks)includes/Api/Subscription.php(2 hunks)package.json(1 hunks)tailwind.config.js(1 hunks)templates/subscriptions/listing.php(2 hunks)templates/subscriptions/pack-details.php(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- assets/js/stores/notice.js
 - package.json
 
🧰 Additional context used
🧬 Code graph analysis (2)
templates/subscriptions/pack-details.php (1)
wpuf-functions.php (3)
wpuf_get_option(1535-1543)wpuf_format_price(2870-2900)wpuf_is_checkbox_or_toggle_on(5592-5594)
includes/Api/Subscription.php (1)
wpuf-functions.php (1)
wpuf_get_option(1535-1543)
🪛 PHPMD (2.15.0)
includes/Api/Subscription.php
559-559: Avoid unused parameters such as '$request'. (undefined)
(UnusedFormalParameter)
🔇 Additional comments (8)
includes/Api/Subscription.php (2)
101-116: LGTM: Dedicated REST endpoints for subscription settings are well-scoped.Clear separation from package CRUD; permission callback is consistent with the rest of the controller.
Also, linked issue #993 (product_cat in emails) isn’t touched in this file set. Please confirm where that fix landed or if it’s coming in a separate PR to avoid scope creep.
577-606: Validation flow looks correct; returns consistent 400 on bad hex.Good job rejecting invalid colors and avoiding null in the option.
Note: the UI falls back to Tailwind “primary” utilities when empty. Ensure Tailwind defines
primary/primaryHover(see tailwind.config.js comment) or the fallback classes won’t exist.tailwind.config.js (2)
5-9: Include includes//*.php in content globs to avoid purging classes used in PHP.**Classes emitted from server-side PHP under includes/ will be purged otherwise.
module.exports = { prefix: 'wpuf-', content: [ './templates/**/*.php', './admin/form-builder/views/*.php', './src/**/*.{js,css}', './assets/js/**/*.{js,vue}', + './includes/**/*.php', ],
11-12: Define primary/primaryHover tokens; many templates/classes rely on them.Add theme.extend.colors.primary and primaryHover (and require 'tailwindcss/colors') so utilities like wpuf-bg-primary, hover:wpuf-bg-primaryHover, wpuf-text-primary and focus ring colors are generated.
+const colors = require('tailwindcss/colors'); /** @type {import('tailwindcss').Config} */ module.exports = { prefix: 'wpuf-', content: [ './templates/**/*.php', './admin/form-builder/views/*.php', './src/**/*.{js,css}', './assets/js/**/*.{js,vue}', ], theme: { - extend: {}, + extend: { + colors: { + primary: colors.emerald['600'], + primaryHover: colors.emerald['700'], + }, + }, }, plugins: [ require('@tailwindcss/forms'), ], }rg in the sandbox returned no matches for wpuf-*primary — verify local templates if unsure.
templates/subscriptions/listing.php (1)
29-29: LGTM: Responsive grid classes read well and match the 1–2–3 col layout.assets/js/components/subscriptions/Preferences.vue (2)
36-65: Add quick client-side hex validation for better UX.Prevents needless round-trips and gives instant feedback.
const saveSettings = async () => { - isSaving.value = true; + // Allow empty for default; otherwise require #RRGGBB + if (settings.value.button_color && !/^#[0-9a-fA-F]{6}$/.test(settings.value.button_color)) { + noticeStore.addNotice({ + type: 'error', + message: __('Please enter a valid 6‑digit hex color (e.g., #4f46e5) or leave empty to use the default.', 'wp-user-frontend') + }); + return; + } + isSaving.value = true;
116-125: Primary classes depend on Tailwind tokens.
wpuf-bg-primary/hover:wpuf-bg-primaryHoverneed config support; see tailwind.config.js fix.templates/subscriptions/pack-details.php (1)
112-115: Fix URL escaping order for add_query_arg.Pass raw URL to add_query_arg and escape the final href.
- href="<?php echo esc_attr( add_query_arg( $query_args, esc_url( $query_url ) ) ); ?>" + href="<?php echo esc_url( add_query_arg( $query_args, $query_url ) ); ?>"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please check the existing comments
…iew-993 Resolved conflicts: - includes/Api/Subscription.php: Merged both subscription settings endpoints and taxonomy view restriction functionality - assets/js/subscriptions.min.js: Kept deleted (removed as part of Tailwind refactoring) - assets/js/subscriptions.min.js.map: Kept deleted (removed as part of Tailwind refactoring) - assets/js/forms-list.min.js: Accepted upstream changes (will be regenerated with build)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
includes/Api/Subscription.php (2)
568-575: Consider suppressing the unused parameter warning.The
$requestparameter is unused but required by the REST API callback signature. While functionally correct, you can optionally prefix it with an underscore ($_request) or add a@SuppressWarningsannotation to silence the PHPMD warning.Example:
- public function get_subscription_settings( $request ) { + public function get_subscription_settings( $_request ) {
586-615: Validation logic looks solid and addresses previous review concerns.The method now properly validates the sanitized color and returns a clear error for invalid input. The handling of empty strings as a signal to use the Tailwind default is well-documented.
Optional refinement: Line 595 checks both
nulland empty string, butsanitize_hex_color()only returnsnullon failure. The empty string check is redundant since line 591 already filters out empty inputs. You could simplify to:- if ( $sanitized_color === null || $sanitized_color === '' ) { + if ( $sanitized_color === null ) {However, the defensive check is harmless and explicit, so this is purely optional.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
includes/Api/Subscription.php(2 hunks)includes/Assets.php(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- includes/Assets.php
 
🧰 Additional context used
🧬 Code graph analysis (1)
includes/Api/Subscription.php (1)
wpuf-functions.php (1)
wpuf_get_option(1535-1543)
🪛 PHPMD (2.15.0)
includes/Api/Subscription.php
568-568: Avoid unused parameters such as '$request'. (undefined)
(UnusedFormalParameter)
🔇 Additional comments (1)
includes/Api/Subscription.php (1)
102-116: LGTM! Endpoint registration follows established patterns.The new
/subscription-settingsroute is properly registered with appropriate HTTP methods and permission checks. The structure is consistent with other endpoints in this controller.
* feat(subscription): implement Tailwind UI-based layout for packs view * fix: design cjange * fix: Fix subscription pack 'See more' button affecting multiple cards - Moved JavaScript toggle function from pack-details.php to listing.php - Function is now defined only once instead of being redefined for each pack - Added unique IDs for each pack's elements to ensure proper isolation - Added null checks to prevent errors if elements are not found - Each subscription pack card now expands/collapses independently This fixes the issue where clicking "See more" on one subscription pack would expand all packs simultaneously. Now each pack's features list expands and collapses independently as expected. 🤖 Generated with Claude Code Co-Authored-By: Claude <[email protected]> * fix: Improve subscription pack toggle to use event delegation - Replaced onclick handlers with data attributes and event delegation - Added unique data-wpuf-pack-id attributes for proper isolation - Implemented event listener on document level to handle all toggles - Added data-expanded attribute to track button state - Prevents multiple event listener registrations with initialization flag - Each pack now properly expands/collapses independently This ensures that clicking "See more" on one subscription pack only affects that specific pack, not others on the page. 🤖 Generated with Claude Code Co-Authored-By: Claude <[email protected]> * feat : added button color customization * change the settings--->preferences * fix: coderabit issues * chore: yoda fuctions applied * fix tailwind config * fix: color fix * chore : remove unused library * fix: primary emerald color * fix: import colors in tailwind.config.js and rebuild forms-list.min.js --------- Co-authored-by: Claude <[email protected]>
* feat(subscription): implement Tailwind UI-based layout for packs view * fix: design cjange * fix: Fix subscription pack 'See more' button affecting multiple cards - Moved JavaScript toggle function from pack-details.php to listing.php - Function is now defined only once instead of being redefined for each pack - Added unique IDs for each pack's elements to ensure proper isolation - Added null checks to prevent errors if elements are not found - Each subscription pack card now expands/collapses independently This fixes the issue where clicking "See more" on one subscription pack would expand all packs simultaneously. Now each pack's features list expands and collapses independently as expected. 🤖 Generated with Claude Code Co-Authored-By: Claude <[email protected]> * fix: Improve subscription pack toggle to use event delegation - Replaced onclick handlers with data attributes and event delegation - Added unique data-wpuf-pack-id attributes for proper isolation - Implemented event listener on document level to handle all toggles - Added data-expanded attribute to track button state - Prevents multiple event listener registrations with initialization flag - Each pack now properly expands/collapses independently This ensures that clicking "See more" on one subscription pack only affects that specific pack, not others on the page. 🤖 Generated with Claude Code Co-Authored-By: Claude <[email protected]> * feat : added button color customization * change the settings--->preferences * fix: coderabit issues * chore: yoda fuctions applied * fix tailwind config * fix: color fix * chore : remove unused library * fix: primary emerald color * fix: import colors in tailwind.config.js and rebuild forms-list.min.js --------- Co-authored-by: Claude <[email protected]>
* feat(subscription): implement Tailwind UI-based layout for packs view * fix: design cjange * fix: Fix subscription pack 'See more' button affecting multiple cards - Moved JavaScript toggle function from pack-details.php to listing.php - Function is now defined only once instead of being redefined for each pack - Added unique IDs for each pack's elements to ensure proper isolation - Added null checks to prevent errors if elements are not found - Each subscription pack card now expands/collapses independently This fixes the issue where clicking "See more" on one subscription pack would expand all packs simultaneously. Now each pack's features list expands and collapses independently as expected. 🤖 Generated with Claude Code Co-Authored-By: Claude <[email protected]> * fix: Improve subscription pack toggle to use event delegation - Replaced onclick handlers with data attributes and event delegation - Added unique data-wpuf-pack-id attributes for proper isolation - Implemented event listener on document level to handle all toggles - Added data-expanded attribute to track button state - Prevents multiple event listener registrations with initialization flag - Each pack now properly expands/collapses independently This ensures that clicking "See more" on one subscription pack only affects that specific pack, not others on the page. 🤖 Generated with Claude Code Co-Authored-By: Claude <[email protected]> * feat : added button color customization * change the settings--->preferences * fix: coderabit issues * chore: yoda fuctions applied * fix tailwind config * fix: color fix * chore : remove unused library * fix: primary emerald color * fix: import colors in tailwind.config.js and rebuild forms-list.min.js --------- Co-authored-by: Claude <[email protected]>
* Add WPUF post form fields reference documentation Introduces a comprehensive Markdown reference for all available WPUF (WordPress User Frontend) post form fields, including field types, properties, options, Pro vs Free distinctions, validation rules, templates, and usage guidelines for AI-driven form creation * feat: initialization of ai form builder design * feat: check box design refinement * fix: right side form design * fix: move all the php file to vue file with build process * fix: design and responsiveness * fix: conflict * fix: component divided * feat :Added ai form builder implementation and the predefined templates * php library added for ai and mock fortend form working * chore: update implementatoin * fix: mock date field issues * fix issues of template hover css * fix: design of required fields * fix : chat box button logic * chore: some code changes for better readability * feat: added the setting from this branch sapayth:feat/settings_for_ai * fix: updated the model and provide list based on our sdk * big changes: real api call aplied * fix: apply and cancel button * some fix that need to privide best response * chore: cleanup codebase * chore: code cleanup and fix ai form template only for post forms * fix: system prompt fix for ai to give specific answer * chore: added post form field reference md * fix: form not changing view * fix: dropdown svg icon issues * fix: form loading issues in edit in builder option * fix: ai response * fix: ai respones properly * feat: design the modal of regenerate * design fix modal * fix: country fields fix * fix: button disabled while generating response * added pro fields checks in form success stage * feat: added checkpoitns to each responses * fix: radio field issues * fix: design * fix : msg and text area field issus * fix: ai response * feat: added latest model * fix: gotosettings link when the ai settings is not configured * fix : ai response waiting to form builder * fix: ai respose for check box , radio button and dropdowns * fix: design * fix: design * fix: ignore form related questions * fix: remove predefined provider * fix: confetti url fix * fix: broken response * fix: settings onchange * fix: selectted prompt unselect on change prompt input * remove unused file from form generator * fix: settings and md file change * big changes: updated prompt and code refactor * feat: add new button for ai form builder * fix: waarnind added properly * remove test file * fix: coderabbit issues and checkpoints * fix: ai form builder design cs s * fix: code rabbit issues * fix the admin css and dynamic link * feat: ui desing fix * fix: last resotore point not show * fix: gif issues and other feeddback * fix: post_type_object type error ' * chore : remove var dump * fix: ai stage check and updated model * fix: property form prompts * chore: remove erro log * chore: coderabbit issues * fix: generalized the prompt * feat: added registration ai form builder * fix: ai return msg * fix: response ai * fix : all console logs * fix: Updated the localization handle - Changed wp_localize_script() from 'wpuf-admin-script' to 'wpuf-admin' to match the registered script handle * fix: ai model list in one place * fix: file exists check for generator * chore: rest controller optimization * chore: AI_Manager fix * fix: 00% Consistent Naming - All components now use rest_url (snake_case) for the REST API URL property across the entire codebase * fix: resolve conflicts * Enhancement/tailwind subscription packs view 993 (#1689) * feat(subscription): implement Tailwind UI-based layout for packs view * fix: design cjange * fix: Fix subscription pack 'See more' button affecting multiple cards - Moved JavaScript toggle function from pack-details.php to listing.php - Function is now defined only once instead of being redefined for each pack - Added unique IDs for each pack's elements to ensure proper isolation - Added null checks to prevent errors if elements are not found - Each subscription pack card now expands/collapses independently This fixes the issue where clicking "See more" on one subscription pack would expand all packs simultaneously. Now each pack's features list expands and collapses independently as expected. 🤖 Generated with Claude Code Co-Authored-By: Claude <[email protected]> * fix: Improve subscription pack toggle to use event delegation - Replaced onclick handlers with data attributes and event delegation - Added unique data-wpuf-pack-id attributes for proper isolation - Implemented event listener on document level to handle all toggles - Added data-expanded attribute to track button state - Prevents multiple event listener registrations with initialization flag - Each pack now properly expands/collapses independently This ensures that clicking "See more" on one subscription pack only affects that specific pack, not others on the page. 🤖 Generated with Claude Code Co-Authored-By: Claude <[email protected]> * feat : added button color customization * change the settings--->preferences * fix: coderabit issues * chore: yoda fuctions applied * fix tailwind config * fix: color fix * chore : remove unused library * fix: primary emerald color * fix: import colors in tailwind.config.js and rebuild forms-list.min.js --------- Co-authored-by: Claude <[email protected]> * Merge upstream/develop and fix form-builder CSS issue (#1718) * merge conflict remove * fix: address field not working * fix: category dropdown * fix: prompts * fix: registration form ai form * fix: ai form builder fix: * fix: predefined prompt * fix: country field * big changes: optimization and other fix * fix : registration forms fields * fix: bio and profile fields * fix: ai response field options * fix: ai form builder restore points * fix: google map and bio field * chore: remove debug log * fix: google map error: * fix: desing settings * fix: options show * fix: select , multi select and radio button fix in ai form builder * fix: show data in post and hide field label in pst for ai form * fix: max fiel and file size missing default valuw * fix: embed field integration * fix: issues * fix: issues * fix: shortcode ai generated dynamic * fix: profile photo and avatar photo * fix: ai form builder column fields check * fix: column fields slider issues * fix: language change in ai form builder * fix: shortcode fix * fix: shortcode * fix: ai form builder issue * fix: social and profile field * fix: socil field meta key --------- Co-authored-by: Sapayth Hossain <[email protected]> Co-authored-by: Claude <[email protected]>

Closes #993
Related PR
🔹 1. Template Updates (Frontend UI)
templates/subscriptions/pack-details.phpBefore: Legacy
div-based boxes with inline/old CSS.After: Modern Tailwind-styled cards:
wpuf-rounded-xl wpuf-p-6 wpuf-shadow-md hover:wpuf-shadow-lg→ Rounded corners + hover effect.wpuf-flex wpuf-flex-col wpuf-h-full→ Ensures equal card heights with flexible content alignment.wpuf-mt-2) for breathing room.👉 Result: Professional pricing cards with responsive, consistent styling.
templates/subscriptions/listing.phpBefore: Non-responsive, inconsistent grid.
After: Tailwind responsive grid system:
wpuf-grid-cols-1 md:wpuf-grid-cols-2 lg:wpuf-grid-cols-3→ adapts across devices.wpuf-items-stretch→ ensures all cards align in height.wpuf-max-w-5xl wpuf-mx-auto→ centers layout, prevents stretching too wide.👉 Result: Polished subscription listing grid (mobile → 1 col, tablet → 2 col, desktop → 3 col).
🔹 2. CSS Cleanup & Optimization
src/css/frontend/subscriptions.cssBefore: 320+ lines of legacy overrides.
After (~80 lines):
@applyutilities.ul.wpuf_packs { margin-top: 30px !important; }for spacing issue.👉 Result: Minimal, maintainable CSS with Tailwind-driven styling.
src/css/admin/form-builder.cssFixed invalid Tailwind utilities:
wpuf-bg-primary → wpuf-bg-emerald-600wpuf-shadow-primary → wpuf-shadow-smwpuf-shadow-transparent → wpuf-shadow-none👉 Result: No more Tailwind build errors in admin panel.
assets/css/frontend-forms.css.wpuf_packsCSS that conflicted with Tailwind.👉 Result: No conflicts between legacy CSS & modern Tailwind.
🔹 3. Build System Improvements
tailwind.config.jsBefore: 127 lines of complex colors, fonts, plugins.
After: 18 lines with only:
#059669) as primary.prefix: 'wpuf-'→ avoids class conflicts with site themes.👉 Result: Simpler, faster, conflict-free Tailwind builds.
vite.config.mjsSimplified from 58 → 41 lines:
👉 Result: Faster build process with less config overhead.
assets/js/frontend-subscriptions.jsNew Vite entry point:
Ensures subscription CSS is compiled & bundled properly.
👉 Result: Future-proof setup for JS + CSS builds.
🔹 4. Visual & Functional Improvements
Subscription Cards (Before → After)
✅ Rounded, shadowed, responsive cards.
✅ Consistent height with flexbox.
✅ Buttons & badges styled with indigo.
✅ Hover elevation effects.
Grid Layout
✅ Responsive (1–2–3 columns).
✅ Spacing handled by
wpuf-gap-4.✅ Centered max-width container.
Conflicts Resolved
✅ Legacy CSS removed/commented.
✅ Tailwind utilities take full control.
✅ Admin errors fixed.
🔹 5. File Structure Impact
Your new structure makes it clear & maintainable:
🔹 6. Summary Statistics
✅ Final Outcome
You’ve transformed the subscription system into:
👉 This means better user experience on the frontend and easier long-term maintenance for developers. 🚀
Before:

After:

Summary by CodeRabbit
New Features
Style
Chores