Skip to content

Refactor/subscriptions#75

Merged
AustinKelsay merged 7 commits intomainfrom
refactor/subscriptions
May 15, 2025
Merged

Refactor/subscriptions#75
AustinKelsay merged 7 commits intomainfrom
refactor/subscriptions

Conversation

@AustinKelsay
Copy link
Copy Markdown
Owner

@AustinKelsay AustinKelsay commented May 14, 2025

Summary by CodeRabbit

  • New Features

    • Added support for both monthly and yearly subscription plans, including UI selectors and dynamic pricing.
    • Users can now choose their preferred subscription type, with yearly plans offering a discounted rate.
    • Subscription benefits, renewal dates, and payment flows update dynamically based on the selected plan.
  • Enhancements

    • Payment and renewal processes now handle both monthly and yearly subscriptions, including updated expiration logic and API support.
    • All modals across the app now use a unified, custom-styled modal component for a consistent look and feel.
  • Documentation

    • Added documentation outlining the yearly subscription implementation, UI changes, payment logic, and testing plan.

@vercel
Copy link
Copy Markdown

vercel bot commented May 14, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
plebdevs ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 15, 2025 2:57pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented May 14, 2025

Walkthrough

This update introduces support for both monthly and yearly subscription types across the application. It adds a subscriptionType field to the database schema, updates backend logic to handle different subscription durations and renewal logic, and enhances frontend components to allow users to select and manage their subscription plan. A new custom Modal component replaces the previous dialog implementation throughout the UI.

Changes

File(s) Change Summary
prisma/migrations/20250514143724_add_subscription_type/migration.sql
prisma/schema.prisma
Added a non-nullable subscriptionType field with default 'monthly' to the Role model/table.
prisma/migrations/migration_lock.toml Added a trailing newline for formatting consistency.
src/components/ui/Modal.js Introduced a new customizable Modal component wrapping PrimeReact's Dialog, supporting dark theme and various props for consistent modal dialogs across the app.
src/components/MoreInfo.js
src/components/bitcoinConnect/CoursePaymentButton.js
src/components/bitcoinConnect/ResourcePaymentButton.js
src/components/forms/course/LessonSelector.js
src/components/onboarding/WelcomeModal.js
src/components/profile/UserBadges.js
src/components/profile/UserProfileCard.js
src/components/profile/subscription/CalendlyEmbed.js
src/components/profile/subscription/CancelSubscription.js
src/components/profile/subscription/LightningAddressForm.js
src/components/profile/subscription/Nip05Form.js
src/components/profile/subscription/RenewSubscription.js
Replaced all usages of the third-party Dialog component with the new custom Modal component. Adjusted props and styling as needed for compatibility.
src/components/bitcoinConnect/SubscriptionPaymentButton.js Enhanced to support a new subscriptionType prop (default 'monthly'), dynamically adjusting payment amount, UI labels, tracking, API calls, and NWC client configuration based on the selected plan.
src/components/profile/subscription/SubscribeModal.js Refactored to use the new Modal. Added a subscription plan selector (monthly/yearly), updated expiration logic, and passed the selected plan to payment and API update logic.
src/components/profile/subscription/UserSubscription.js Added plan selection UI, dynamic pricing, and expiration calculation based on subscriptionType. Updated FAQ and benefits display to reflect plan choice.
src/db/models/roleModels.js Modified createRole to set subscriptionType (default 'monthly').
src/db/models/userModels.js Updated updateUserSubscription to accept and store subscriptionType. Enhanced findExpiredSubscriptions and expireUserSubscriptions to handle both monthly and yearly subscriptions with distinct expiration logic.
src/pages/api/users/subscription/index.js Updated PUT handler to accept and forward subscriptionType to the update function.
src/pages/api/users/subscription/cron.js Enhanced cron job to process and renew both monthly and yearly subscriptions, with separate expiration periods, amounts, and detailed statistics reporting.
yearly_subscriptions.md Added a detailed implementation plan and testing strategy for supporting yearly subscriptions alongside monthly, covering schema, UI, API, cron job, and marketing considerations.
src/constants/subscriptionPeriods.js Added constants and helper functions to define subscription periods and expiration logic for monthly and yearly plans.
src/pages/about.js Replaced hardcoded subscription duration with dynamic value from subscription periods constants.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI
    participant API
    participant DB
    participant NWC

    User->>UI: Open subscription modal
    UI->>UI: Show plan selector (monthly/yearly)
    User->>UI: Select plan and initiate payment
    UI->>API: PUT /api/users/subscription { subscriptionType, ... }
    API->>DB: updateUserSubscription(userId, isSubscribed, nwc, subscriptionType)
    DB-->>API: Update Role with subscriptionType
    API-->>UI: Success response
    UI->>NWC: Initiate payment with amount based on subscriptionType
    NWC-->>UI: Payment result
    UI-->>User: Show confirmation and updated plan details
Loading
sequenceDiagram
    participant Cron
    participant DB
    participant NWC
    participant API

    Cron->>DB: findExpiredSubscriptions()
    DB-->>Cron: List of expired monthly/yearly subscriptions
    loop For each expired subscription
        Cron->>NWC: Attempt renewal (amount & period based on subscriptionType)
        alt Payment success
            Cron->>API: updateUserSubscription(userId, true, nwc, subscriptionType)
        else Payment failed/missing NWC
            Cron->>API: updateUserSubscription(userId, false, nwc, subscriptionType)
        end
    end
    Cron-->>DB: Expire failed subscriptions
Loading

Poem

🐇
A hop, a leap, a brand new plan,
Now monthly or yearly—choose as you can!
Modals shine in custom light,
Subscriptions flex from month to night.
The backend hums with types anew,
While bunny devs code dreams come true.
Hop on, upgrade—this one's for you!

[!ANNOUNCEMENT]

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.


[!ANNOUNCEMENT]

⚡️ Faster reviews with caching

CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.
Enjoy the performance boost—your workflow just got faster.

✨ Finishing Touches
  • 📝 Generate Docstrings

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
src/db/models/userModels.js (1)

168-192: ⚠️ Potential issue

Add runtime guard & standardise property assignment

subscriptionType is accepted as a free-form string. Persisting an unexpected value will break every piece of logic that relies on the "monthly" / "yearly" dichotomy (cron, pricing, UI toggles, etc.).
You can avoid subtle data corruption by validating the input up-front and by using a uniform “{ set: … }” syntax for update operations:

export const updateUserSubscription = async (userId, isSubscribed, nwc, subscriptionType = 'monthly') => {
+  if (!['monthly', 'yearly'].includes(subscriptionType)) {
+    throw new Error(`Invalid subscriptionType "${subscriptionType}"`);
+  }

   try {
     const now = new Date();
     return await prisma.user.update({
       …
-              lastPaymentAt: isSubscribed ? now : { set: null },
+              lastPaymentAt: { set: isSubscribed ? now : null },
🧹 Nitpick comments (13)
src/pages/api/users/subscription/cron.js (1)

58-58: Consider using optional chaining.

The static analysis tool suggests using optional chaining for better code safety.

-if (response && response?.preimage) {
+if (response?.preimage) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 58-58: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/components/profile/subscription/UserSubscription.js (2)

44-44: Consider using optional chaining.

The static analysis tool suggests using optional chaining for better code safety.

-if (session && session?.user) {
+if (session?.user) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 44-44: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


53-53: Consider using optional chaining.

The static analysis tool suggests using optional chaining for better code safety.

-if (user && user.role) {
+if (user?.role) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 53-53: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/db/models/userModels.js (2)

251-267: Dead code – retrieved subscription types are never used

expireUserSubscriptions() fetches subscriptionType for each userId, builds the subscriptionTypes map, and then discards it. This adds an extra query and memory allocation without any benefit.

If you intended to log / audit which type was expired, use the map; otherwise, delete the whole block:

-    const subscriptions = await prisma.role.findMany({ … });
-    const subscriptionTypes = {};
-    subscriptions.forEach(sub => {
-      subscriptionTypes[sub.userId] = sub.subscriptionType || 'monthly';
-    });

196-202: Connection management inconsistency

This function (and two others below) calls prisma.$disconnect() in a finally block, whereas all read/update helpers above keep the connection open.
Frequent connects/disconnects can exhaust connection pools in serverless environments and slow every request.

Align the strategy: either always disconnect at the API-layer boundary or rely on a singleton Prisma client that manages its own lifecycle.

src/components/profile/subscription/SubscribeModal.js (2)

259-282: Hard-coded price strings hinder future pricing changes

Displaying "50,000" / "500,000" directly couples UI to pricing logic. A single forgotten magic number could produce inconsistent pricing displays when marketing runs promotions.

Expose a constant or utility function that derives the display amount from the same source used for invoice generation:

import { getAmountForPlan } from '@/utils/subscriptionPricing';


<div className="price-display …">
  {getAmountForPlan(subscriptionType).toLocaleString()} sats

177-187: Copy text still defaults to “monthly”

The fallback || 'monthly' in the status messages will incorrectly label yearly subscribers if subscriptionType is accidentally null but the duration is 365 days.
Prefer showing an explicit “unknown” or—better—failing early when subscriptionType is not set, so that data issues surface during QA.

yearly_subscriptions.md (1)

193-195: Minor grammar – missing article

“consider grandfathering existing subscribers or offering an upgrade path”

🧰 Tools
🪛 LanguageTool

[uncategorized] ~195-~195: You might be missing the article “an” here.
Context: ...hering existing subscribers or offering upgrade path

(AI_EN_LECTOR_MISSING_DETERMINER_AN)

src/components/ui/Modal.js (4)

48-63: Width handling could be more robust.

The width handling logic is functional, but lacks validation for custom width values. Consider adding validation to ensure that custom width values are valid CSS dimensions.

 // Determine width class
 let widthClass = '';
 if (width === 'fit') {
   widthClass = 'w-fit';
 } else if (width === 'full') {
   widthClass = 'w-full';
 } else {
   // Custom width will be handled via style
+  // Ensure width is a valid CSS dimension
+  const isValidDimension = /^\d+(%|px|rem|em|vh|vw)?$/.test(width);
+  if (isValidDimension) {
     style.width = width;
+  } else {
+    console.warn(`Invalid width value: ${width}. Using default width.`);
+  }
 }

77-84: Global CSS could cause style conflicts.

Using global CSS with generic selectors could affect other PrimeReact Dialog components in the application. Consider using a more targeted approach or component-specific styles.

- // Apply tailwind CSS to modify dialog elements
- const dialogClassNames = `
-   .p-dialog-footer {
-     background-color: #1f2937 !important;
-     border-top: 1px solid #374151 !important;
-   }
- `;

+ // Use a unique class for styling this specific modal type
+ const modalClassName = `dark-modal-${Math.random().toString(36).substr(2, 9)}`;
+ const dialogClassNames = `
+   .${modalClassName} .p-dialog-footer {
+     background-color: #1f2937 !important;
+     border-top: 1px solid #374151 !important;
+   }
+ `;

Then update the Dialog className to include this unique class:

 <Dialog
   header={header}
   visible={visible}
   onHide={onHide}
-  className={`p-fluid pb-0 ${widthClass} ${className}`}
+  className={`p-fluid pb-0 ${widthClass} ${modalClassName} ${className}`}
   style={{ ...baseStyle, ...style }}

86-109: Consider enhancing accessibility attributes.

While the underlying Dialog component may handle some accessibility features, explicitly adding ARIA attributes would improve the component's accessibility.

 <Dialog
   header={header}
   visible={visible}
   onHide={onHide}
   className={`p-fluid pb-0 ${widthClass} ${className}`}
   style={{ ...baseStyle, ...style }}
   headerStyle={{ ...baseHeaderStyle, ...headerStyle }}
   contentStyle={{ ...baseContentStyle, ...contentStyle }}
   footerStyle={{ ...baseFooterStyle, ...footerStyle }}
   footer={footerContent}
   breakpoints={breakpoints}
   modal={modal}
   draggable={draggable}
   resizable={resizable}
   maximizable={maximizable}
   dismissableMask={dismissableMask}
+  aria-modal={true}
+  aria-labelledby={header ? 'modal-header' : undefined}
   {...otherProps}
 >
+  {header && <span id="modal-header" className="sr-only">{header}</span>}
   {children}
 </Dialog>

77-108: Performance optimization for global CSS.

The global CSS is recreated on each render. Consider defining it once outside the component for better performance.

+// Define global styles outside the component to prevent recreation on every render
+const dialogClassNames = `
+  .p-dialog-footer {
+    background-color: #1f2937 !important;
+    border-top: 1px solid #374151 !important;
+  }
+`;

const Modal = ({
  header,
  visible,
  onHide,
  children,
  className = '',
  style = {},
  width = 'fit',
  footer,
  headerStyle = {},
  contentStyle = {},
  footerStyle = {},
  breakpoints,
  modal,
  draggable,
  resizable,
  maximizable,
  dismissableMask,
  showCloseButton = false,
  ...otherProps
}) => {
  // Base dark styling
  const baseStyle = { backgroundColor: '#1f2937' };
  const baseHeaderStyle = { backgroundColor: '#1f2937', color: 'white' };
  const baseContentStyle = { backgroundColor: '#1f2937' };
  const baseFooterStyle = { backgroundColor: '#1f2937', borderTop: '1px solid #374151' };
  
  // Determine width class
  let widthClass = '';
  if (width === 'fit') {
    widthClass = 'w-fit';
  } else if (width === 'full') {
    widthClass = 'w-full';
  } else {
    // Custom width will be handled via style
    style.width = width;
  }

  // Create footer with close button if requested
  const footerContent = showCloseButton ? (
    <div className="flex justify-end w-full">
      <Button 
        label="Close" 
        icon="pi pi-times" 
        onClick={onHide} 
        className="p-button-text text-white"
      />
    </div>
  ) : footer;

-  // Apply tailwind CSS to modify dialog elements
-  const dialogClassNames = `
-    .p-dialog-footer {
-      background-color: #1f2937 !important;
-      border-top: 1px solid #374151 !important;
-    }
-  `;

  return (
    <>
      <style jsx global>{dialogClassNames}</style>
prisma/schema.prisma (1)

86-86: Consider defining subscriptionType as a Prisma enum
Using a raw String permits invalid values at runtime. Introducing an enum enforces the allowed set (monthly, yearly) at the schema and client level:

enum SubscriptionType {
  monthly
  yearly
}

model Role {
  subscriptionType SubscriptionType @default(monthly)
  // ...
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6423579 and 2c6c230.

📒 Files selected for processing (24)
  • prisma/migrations/20250514143724_add_subscription_type/migration.sql (1 hunks)
  • prisma/migrations/migration_lock.toml (1 hunks)
  • prisma/schema.prisma (1 hunks)
  • src/components/MoreInfo.js (2 hunks)
  • src/components/bitcoinConnect/CoursePaymentButton.js (3 hunks)
  • src/components/bitcoinConnect/ResourcePaymentButton.js (3 hunks)
  • src/components/bitcoinConnect/SubscriptionPaymentButton.js (12 hunks)
  • src/components/forms/course/LessonSelector.js (2 hunks)
  • src/components/onboarding/WelcomeModal.js (3 hunks)
  • src/components/profile/UserBadges.js (3 hunks)
  • src/components/profile/UserProfileCard.js (2 hunks)
  • src/components/profile/subscription/CalendlyEmbed.js (3 hunks)
  • src/components/profile/subscription/CancelSubscription.js (3 hunks)
  • src/components/profile/subscription/LightningAddressForm.js (3 hunks)
  • src/components/profile/subscription/Nip05Form.js (3 hunks)
  • src/components/profile/subscription/RenewSubscription.js (3 hunks)
  • src/components/profile/subscription/SubscribeModal.js (7 hunks)
  • src/components/profile/subscription/UserSubscription.js (8 hunks)
  • src/components/ui/Modal.js (1 hunks)
  • src/db/models/roleModels.js (1 hunks)
  • src/db/models/userModels.js (5 hunks)
  • src/pages/api/users/subscription/cron.js (1 hunks)
  • src/pages/api/users/subscription/index.js (1 hunks)
  • yearly_subscriptions.md (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
src/components/profile/subscription/LightningAddressForm.js (1)
src/components/ui/Modal.js (1)
  • Modal (27-110)
src/components/profile/subscription/RenewSubscription.js (1)
src/components/ui/Modal.js (1)
  • Modal (27-110)
src/components/forms/course/LessonSelector.js (4)
src/components/ui/Modal.js (1)
  • Modal (27-110)
src/components/forms/course/embedded/EmbeddedDocumentForm.js (1)
  • EmbeddedDocumentForm (15-248)
src/components/forms/course/CourseForm.js (1)
  • isPaidCourse (21-21)
src/components/forms/course/embedded/EmbeddedVideoForm.js (1)
  • EmbeddedVideoForm (12-228)
src/components/profile/subscription/UserSubscription.js (12)
src/components/profile/subscription/SubscribeModal.js (3)
  • subscriptionType (37-37)
  • subscriptionOptions (39-42)
  • session (23-23)
src/components/profile/subscription/Nip05Form.js (2)
  • session (18-18)
  • windowWidth (17-17)
src/components/profile/subscription/LightningAddressForm.js (2)
  • session (25-25)
  • windowWidth (24-24)
src/components/bitcoinConnect/SubscriptionPaymentButton.js (3)
  • session (34-34)
  • getAmount (40-42)
  • SubscriptionPaymentButtons (20-351)
src/components/navbar/Navbar.js (2)
  • session (20-20)
  • windowWidth (18-18)
src/components/feeds/MessageInput.js (1)
  • session (17-17)
src/pages/details/[slug]/index.js (1)
  • session (28-28)
src/components/profile/UserProfile.js (3)
  • session (19-19)
  • user (17-17)
  • windowWidth (16-16)
src/components/profile/UserContent.js (3)
  • session (27-27)
  • user (28-28)
  • windowWidth (26-26)
src/pages/api/users/subscription/cron.js (1)
  • getAmount (12-15)
src/components/profile/subscription/CalendlyEmbed.js (1)
  • windowWidth (7-7)
src/components/profile/UserProfileCard.js (1)
  • windowWidth (20-20)
🪛 Biome (1.9.4)
src/components/profile/subscription/UserSubscription.js

[error] 44-44: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 53-53: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/pages/api/users/subscription/cron.js

[error] 58-58: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/components/profile/subscription/SubscribeModal.js

[error] 45-45: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🪛 LanguageTool
yearly_subscriptions.md

[grammar] ~22-~22: Using ‘plenty’ without ‘of’ is considered to be informal.
Context: ...dd subscription type parameter - Modify amount calculation based on subscription type - Update NWC...

(PLENTY_OF_NOUNS)


[style] ~131-~131: This adverb was used twice in the sentence. Consider removing one of them or replacing them with a synonym.
Context: ...Subscription UI** - Verify selecting yearly plan updates all UI elements - Check...

(ADVERB_REPETITION_PREMIUM)


[uncategorized] ~163-~163: You might be missing the article “a” here.
Context: ...ctive Yearly Subscription** - Set up test account with yearly subscription - V...

(AI_EN_LECTOR_MISSING_DETERMINER_A)


[uncategorized] ~167-~167: You might be missing the article “a” here.
Context: ...ired Monthly Subscription** - Create test account with monthly subscription - ...

(AI_EN_LECTOR_MISSING_DETERMINER_A)


[uncategorized] ~168-~168: You might be missing the article “the” here.
Context: ...nthly subscription - Manually adjust lastPaymentAt date to be >30 days ago - Run cron j...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~169-~169: You might be missing the article “the” here.
Context: ...0 days ago - Run cron job and verify subscription is expired 4. **Expired Yearly Subscri...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~172-~172: You might be missing the article “a” here.
Context: ...pired Yearly Subscription** - Create test account with yearly subscription - M...

(AI_EN_LECTOR_MISSING_DETERMINER_A)


[uncategorized] ~172-~172: You might be missing the article “a” here.
Context: ...ription** - Create test account with yearly subscription - Manually adjust lastP...

(AI_EN_LECTOR_MISSING_DETERMINER_A)


[uncategorized] ~173-~173: You might be missing the article “the” here.
Context: ...early subscription - Manually adjust lastPaymentAt date to be >365 days ago - Run cron ...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~174-~174: You might be missing the article “the” here.
Context: ...5 days ago - Run cron job and verify subscription is expired 5. Auto-renewal Testing...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~195-~195: You might be missing the article “an” here.
Context: ...hering existing subscribers or offering upgrade path

(AI_EN_LECTOR_MISSING_DETERMINER_AN)

🔇 Additional comments (73)
src/db/models/roleModels.js (1)

9-9: Good implementation of subscriptionType in role creation.

The added field correctly uses the provided value or falls back to 'monthly', maintaining backward compatibility with existing code that doesn't specify a subscription type.

src/pages/api/users/subscription/index.js (1)

15-16: API endpoint updated to support subscription types.

The endpoint now extracts an optional subscriptionType parameter from the request body with a default value of 'monthly' and passes it to the updateUserSubscription function. This implementation ensures backward compatibility with clients that don't specify a subscription type.

src/components/profile/subscription/LightningAddressForm.js (2)

2-2: Modal component import updated.

The Dialog component from PrimeReact has been replaced with a custom Modal component for a more consistent UI.


114-119: Modal usage and props updated.

The Dialog component has been replaced with the custom Modal component and the width prop format has been updated to use the direct width property instead of a style object.

src/components/profile/UserProfileCard.js (2)

4-4: Modal component import updated.

The Dialog component from PrimeReact has been replaced with a custom Modal component for a more consistent UI.


287-293: Modal props restructured.

The Dialog component has been replaced with the custom Modal component, and the props have been restructured:

  • Added width="full" prop
  • Moved the max-width constraint to the className
  • Removed the modal prop (now handled internally by the Modal component)

This change is consistent with the new Modal component's API.

src/components/profile/subscription/CancelSubscription.js (2)

2-2: Modal component import updated.

The Dialog component from PrimeReact has been replaced with a custom Modal component for a more consistent UI.


34-38: Modal component usage updated.

The Dialog component has been replaced with the custom Modal component while preserving the same functionality and props structure.

src/components/bitcoinConnect/CoursePaymentButton.js (2)

3-3: Import updated to use custom Modal component

The import has been updated to use the custom Modal component from @/components/ui/Modal instead of the PrimeReact Dialog component.


230-246: Modal implementation looks good

The Dialog component has been replaced with the custom Modal component, maintaining the same functionality while adopting the standardized modal UI. The width property is now directly passed as a prop rather than in a style object, which aligns with the Modal component's API.

src/components/profile/subscription/RenewSubscription.js (2)

2-2: Import updated to use custom Modal component

The import has been updated to use the custom Modal component from @/components/ui/Modal instead of the PrimeReact Dialog component.


53-83: Modal implementation looks good

The Dialog component has been replaced with the custom Modal component, maintaining the same functionality. The className prop that might have been present in the previous Dialog implementation has been removed, which is consistent with the Modal component's styling approach that applies base styles automatically.

src/components/profile/subscription/Nip05Form.js (2)

2-2: Import updated to use custom Modal component

The import has been updated to use the custom Modal component from @/components/ui/Modal instead of the PrimeReact Dialog component.


85-90: Modal implementation looks good

The Dialog component has been replaced with the custom Modal component. The width property is now directly passed as a prop with the same responsive logic based on windowWidth, which aligns with the Modal component's API.

src/components/MoreInfo.js (2)

2-2: Import updated to use custom Modal component

The import has been updated to use the custom Modal component from @/components/ui/Modal instead of the PrimeReact Dialog component.


27-35: Modal implementation looks good

The Dialog component has been replaced with the custom Modal component while preserving all props and children. This standardizes the modal UI across the application while maintaining the same functionality.

src/components/onboarding/WelcomeModal.js (3)

1-4: Import changes look good

The change from using PrimeReact's Dialog directly to the custom Modal component keeps the code consistent with the application-wide refactoring.


28-35: Modal component implementation looks good

The component successfully migrates from Dialog to Modal, appropriately splitting width handling into a dedicated width prop (90vw) and maxWidth style property. This aligns with the new Modal component's API design.


78-78: Closing tag change is correct

Properly updated the closing tag to match the new Modal component.

src/components/forms/course/LessonSelector.js (3)

1-8: Import changes look good

The change from using PrimeReact's Dialog directly to the custom Modal component keeps the code consistent with the application-wide refactoring of subscription-related UI.


236-243: Document form modal implementation looks good

The component successfully migrates from Dialog to Modal for the document form modal while maintaining all the necessary props. The className for width control is preserved.


245-252: Video form modal implementation looks good

The component successfully migrates from Dialog to Modal for the video form modal while maintaining all the necessary props. The className for width control is preserved.

src/components/profile/subscription/CalendlyEmbed.js (3)

1-5: Import changes look good

The change from using PrimeReact's Dialog directly to the custom Modal component aligns with the application-wide UI standardization effort.


28-34: Modal implementation with responsive width looks good

The component successfully migrates from Dialog to Modal and properly converts the width styling to use the new Modal's width prop. The responsive behavior based on window width is maintained correctly.


40-40: Closing tag change is correct

Properly updated the closing tag to match the new Modal component.

src/components/profile/UserBadges.js (3)

1-8: Import changes look good

The change from using PrimeReact's Dialog directly to the custom Modal component keeps the code consistent with the application-wide refactoring.


102-108: Modal implementation with width props looks good

The component successfully migrates from Dialog to Modal, with an improved approach to width handling. The width CSS property has been moved to the dedicated width prop ("full"), while max-width remains in the className.


156-156: Closing tag change is correct

Properly updated the closing tag to match the new Modal component.

src/components/bitcoinConnect/ResourcePaymentButton.js (2)

3-3: Dialog replaced with custom Modal component.

The component now uses a custom Modal component from '@/components/ui/Modal' instead of the Dialog component from primereact.


125-130: Modal implementation updated.

The Dialog component has been replaced with the custom Modal component, using a direct width prop instead of a style object. This is consistent with the application-wide refactoring to standardize modal UI.

src/pages/api/users/subscription/cron.js (9)

11-15: Subscription amount helper function added.

New utility function to calculate subscription amounts based on subscription type, providing 500 sats for yearly and 50 sats for monthly subscriptions with appropriate comments.


20-31: Enhanced tracking for subscription types.

Added detailed comments explaining the expiration periods and a structured stats object to track subscription statistics separately by subscription type.


35-38: Updated subscription processing to handle subscription types.

Now destructuring the subscription type from expired subscriptions with a default of 'monthly', and tracking processed subscriptions by type. This ensures backward compatibility with existing data.


41-43: Dynamic subscription amount calculation.

Added logging for subscription type and dynamic amount calculation based on the subscription type.


50-56: Enhanced invoice generation with subscription type.

Updated invoice generation to include the subscription type in the comment and dynamic amount, with improved logging.


58-64: Updated subscription renewal with type preservation.

The subscription renewal now preserves the subscription type when updating the user's subscription status, with typed success tracking.

🧰 Tools
🪛 Biome (1.9.4)

[error] 58-58: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


75-77: Added subscription type tracking for failed renewals.

Now tracking failed renewals by subscription type, improving reporting accuracy.


83-86: Enhanced logging with subscription type details.

Updated console logging to include detailed breakdowns by subscription type, making it easier to monitor the system's performance.


88-93: Updated API response with detailed subscription statistics.

The response now includes a comprehensive breakdown of processed, renewed, and expired subscriptions by type, and returns the full stats object for potential further processing.

src/components/profile/subscription/UserSubscription.js (12)

19-19: Added SelectButton component import.

Import for the SelectButton component which will be used to choose between subscription types.


36-41: Added subscription type state and options.

New state variable to track the selected subscription type and defined options for the selection UI.


44-49: Initialize subscription type from user data.

The subscription type is now initialized from the user's role if available, ensuring consistency between the UI and the user's current subscription.

🧰 Tools
🪛 Biome (1.9.4)

[error] 44-44: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


57-60: Dynamic subscription end date calculation.

Updated to calculate the subscription end date based on the subscription type, using 365 days for yearly and 31 days for monthly subscriptions.


74-76: Include subscription type in API updates.

Now passing the subscription type to the API when updating the user's subscription status.


114-117: Added subscription amount helper function.

New function to calculate the subscription amount based on the subscription type, consistent with the cron job implementation.


183-208: Added subscription plan selection UI.

New UI section with a SelectButton for choosing between monthly and yearly plans, displaying pricing and savings information. The UI is well-structured with appropriate styling and responsive design.


216-217: Pass subscription type to payment component.

The selected subscription type is now passed to the SubscriptionPaymentButtons component, ensuring consistent payment processing.


228-231: Updated card styling.

Minor style adjustments to the subscription benefits card.


241-248: Added current plan display.

New UI elements to show the user's current subscription plan type and renewal date, improving user information.


322-327: Updated FAQ for subscription types.

Added new FAQ entry explaining the difference between monthly and yearly subscription plans, highlighting the savings benefit.


332-334: Updated subscription duration explanation.

Clarified the pay-as-you-go subscription duration to reflect the selected plan type.

src/components/bitcoinConnect/SubscriptionPaymentButton.js (16)

28-29: Added subscription type prop.

Added a new prop to support different subscription types with a default of 'monthly'.


39-45: Dynamic amount calculation based on subscription type.

Added a utility function to calculate the payment amount based on subscription type and initialized a constant with the calculated amount.


84-85: Updated invoice comment with subscription type.

The invoice comment now includes the capitalized subscription type, providing better context for the payment.


96-97: Enhanced payment tracking with subscription type.

Updated analytics tracking to include the subscription type for better data insights.


127-130: Updated NWC client configuration.

The NWC client now uses the dynamic amount and sets the budget renewal period based on the subscription type.


174-175: Include subscription type in API update.

Now passing the subscription type to the API when updating the user's subscription status with a recurring payment.


178-179: Enhanced recurring payment tracking.

Updated analytics tracking for recurring subscriptions to include the subscription type.


240-241: Include subscription type in manual NWC setup.

Now passing the subscription type to the API when updating the user's subscription status with a manual NWC setup.


244-245: Enhanced manual payment tracking.

Updated analytics tracking for manual recurring subscriptions to include the subscription type.


268-269: Improved layout classes.

Updated the flex classes to support both row and column layouts based on the layout prop.


272-273: Dynamic amount in button label.

Updated the "Pay as you go" button label to show the dynamic amount based on subscription type.


284-285: Updated button styling.

Modified button class names to support responsive layouts based on the layout prop.


289-290: Dynamic subscription type in button label.

Updated the recurring subscription button label to include the capitalized subscription type.


300-301: Updated button styling.

Modified recurring subscription button class names to support responsive layouts.


321-322: Updated NWC instructions.

The NWC setup instructions now include the dynamic amount and subscription type for clarity.


345-346: Dynamic amount in payment modal title.

Updated the payment modal title to show the dynamic amount based on subscription type.

src/components/ui/Modal.js (5)

1-4: Clean imports with necessary dependencies.

The imports are appropriate for creating a modal component that wraps PrimeReact's Dialog and uses Button for the optional close button.


5-26: Well-documented component with comprehensive JSDoc.

The JSDoc comments provide excellent documentation for the component, clearly explaining each prop and its purpose.


27-47: Props destructuring with appropriate defaults.

The component definition is clean with good default values for optional props.


65-75: Clean conditional footer implementation.

The footer logic elegantly handles both custom footers and the optional close button case.


1-112: Overall excellent modal implementation.

This Modal component is well-designed, flexible, and provides a clean abstraction over PrimeReact's Dialog with consistent dark styling. It will standardize modal usage across the application while supporting the new subscription UI requirements.

prisma/migrations/20250514143724_add_subscription_type/migration.sql (1)

1-2: Migration adds subscriptionType column with default value
The script correctly alters the Role table to include a non-nullable subscriptionType column of type TEXT and backfills existing rows with the 'monthly' default. SQL syntax and quoting align with the Prisma schema.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (5)
src/constants/subscriptionPeriods.js (2)

7-9: Consider leap year for YEARLY period

The yearly period is set to exactly 365 days, which doesn't account for leap years. While the buffer will likely make this a non-issue in most cases, you might consider adding a comment or using a more precise calculation for sensitive timing requirements.


23-24: Consider adding type validation for subscriptionType parameter

While the conditional checks handle 'yearly' vs non-yearly subscriptions, consider adding explicit validation for the subscriptionType parameter to catch potential bugs if an invalid value is passed.

export const hasSubscriptionExpired = (lastPaymentDate, subscriptionType) => {
  if (!lastPaymentDate) return true;
+  
+  if (subscriptionType !== 'monthly' && subscriptionType !== 'yearly') {
+    console.warn(`Invalid subscriptionType: ${subscriptionType}, defaulting to 'monthly'`);
+    subscriptionType = 'monthly';
+  }
  
  const now = new Date();
src/db/models/userModels.js (1)

255-272: Subscription type map is created but not used

You're fetching subscription types and creating a map (subscriptionTypes), but it doesn't appear to be used in the subsequent update operations. This could be cleaned up if not needed.

-  // First, get the subscription types for each userId
-  const subscriptions = await prisma.role.findMany({
-    where: {
-      userId: { in: userIds },
-    },
-    select: {
-      userId: true,
-      subscriptionType: true,
-    },
-  });
-  
-  // Create a map of userId to subscription type
-  const subscriptionTypes = {};
-  subscriptions.forEach(sub => {
-    subscriptionTypes[sub.userId] = sub.subscriptionType || 'monthly';
-  });

Or if there's a specific reason to preserve this code, consider adding a more descriptive comment explaining its purpose for future maintainers.

src/components/profile/subscription/SubscribeModal.js (2)

45-68: Improve optional chaining in user property access

The user object access should use optional chaining to avoid potential runtime errors if the user or user.role is undefined.

useEffect(() => {
-  if (user && user.role) {
+  if (user?.role) {
    setSubscribed(user.role.subscribed);
    setSubscriptionType(user.role.subscriptionType || 'monthly');
🧰 Tools
🪛 Biome (1.9.4)

[error] 46-46: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


280-282: Calculate savings percentage dynamically

The savings message for yearly subscriptions shows "~17%" as a hardcoded value. Consider calculating this dynamically based on the actual prices to ensure it remains accurate if prices change.

{subscriptionType === 'yearly' && (
  <div className="savings-message text-sm text-green-500 font-semibold mt-2">
-    Save ~17% with yearly subscription!
+    Save {Math.round((1 - (500000 / (50000 * 12))) * 100)}% with yearly subscription!
  </div>
)}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c6c230 and dc359dd.

📒 Files selected for processing (3)
  • src/components/profile/subscription/SubscribeModal.js (7 hunks)
  • src/constants/subscriptionPeriods.js (1 hunks)
  • src/db/models/userModels.js (6 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/constants/subscriptionPeriods.js (1)
src/components/profile/subscription/SubscribeModal.js (1)
  • subscriptionType (38-38)
src/db/models/userModels.js (1)
src/constants/subscriptionPeriods.js (2)
  • SUBSCRIPTION_PERIODS (2-11)
  • SUBSCRIPTION_PERIODS (2-11)
🪛 Biome (1.9.4)
src/components/profile/subscription/SubscribeModal.js

[error] 46-46: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (18)
src/constants/subscriptionPeriods.js (3)

1-11: SUBSCRIPTION_PERIODS constants are well-defined.

The structure defined here provides a clear, centralized location for subscription-related constants, which will help maintain consistency across the application. This is a good approach for ensuring that subscription periods are uniform throughout the codebase.


13-20: Well-designed helper function for expiration date calculation

The calculateExpirationDate function properly encapsulates the logic for determining when a subscription expires, making it reusable across the application. This improves consistency and maintainability.


22-36: Robust subscription expiration check with proper null handling

The hasSubscriptionExpired function includes appropriate null checking for the lastPaymentDate and cleanly handles the subscription type selection. The buffer time inclusion is a good practice for avoiding edge cases around expiration timing.

src/db/models/userModels.js (7)

1-2: Good import of shared subscription constants

Importing the centralized subscription period constants ensures consistency across the application and follows the DRY principle. This is the right approach for maintaining these values.


169-179: Appropriate default for optional subscriptionType parameter

The updateUserSubscription function now accepts an optional subscriptionType parameter defaulting to 'monthly', which is a good backward compatible approach for supporting the new subscription types.


179-180: Properly handling subscriptionType in both create and update operations

The function correctly applies the subscription type to both the create and update operations within the upsert, ensuring consistency in how the subscriptionType is stored.

Also applies to: 187-188


205-219: Well-implemented expiration calculation using shared constants

The function now uses the shared SUBSCRIPTION_PERIODS constants to calculate expiration dates for both monthly and yearly subscriptions, which ensures consistency with other parts of the application.


221-235: Proper query structure for handling multiple subscription types

The query structure using OR conditions is the right approach for handling different subscription types with different expiration periods. This implementation will correctly identify expired subscriptions of both types.


239-240: Good inclusion of subscriptionType in the returned data

Including the subscriptionType in the returned data allows downstream functions to handle different subscription types appropriately.


282-284: Good practice to preserve subscription type

Preserving the subscription type when a subscription expires is a good practice for historical data and makes renewal easier. The comment clearly explains this design decision.

src/components/profile/subscription/SubscribeModal.js (8)

1-2: Good module imports for subscription functionality

The imports of the Modal component and subscription-related utilities (SelectButton and calculateExpirationDate) are appropriate for implementing the subscription type selection feature.

Also applies to: 20-21


38-43: Well-structured subscription type state and options

The initialization of subscription type state and definition of subscription options is clean and follows React best practices. The options structure works well with PrimeReact's SelectButton component.


51-61: Good null handling for lastPaymentAt

The code properly checks for the existence of lastPaymentAt before using it to calculate dates, which prevents errors when dealing with new or expired subscriptions.


54-56: Proper use of shared helper function

Using the imported calculateExpirationDate helper function ensures consistency in how expiration dates are calculated across the application.


70-77: Subscription type correctly included in API request

The function properly includes the selected subscription type when making the API request to update the subscription, ensuring the backend receives this information.


185-186: Dynamic subscription renewal messaging

The renewal messages now dynamically show the subscription type and handle the case where no dates are available with 'N/A'. This improves the user experience by providing accurate information.

Also applies to: 194-195


266-291: Clearly structured subscription plan selector UI

The subscription plan selector UI is well-organized and provides clear feedback to the user about their selection, including pricing and savings information for yearly plans.


293-302: Subscription type correctly passed to payment component

The subscriptionType prop is correctly passed to the SubscriptionPaymentButtons component, allowing it to handle different payment amounts and behavior based on the selected plan.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a 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)
src/components/profile/subscription/UserSubscription.js (2)

45-51: Consider using optional chaining

The conditional check can be simplified using optional chaining.

- if (session && session?.user) {
+ if (session?.user) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 45-45: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


54-67: Consider using optional chaining here too

Similar to the previous suggestion, this conditional can be simplified.

- if (user && user.role) {
+ if (user?.role) {

Additionally, the code correctly uses the shared calculateExpirationDate helper function, which improves code consistency across the application.

🧰 Tools
🪛 Biome (1.9.4)

[error] 54-54: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc359dd and ab7b5fc.

📒 Files selected for processing (2)
  • src/components/profile/subscription/UserSubscription.js (8 hunks)
  • src/pages/about.js (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/components/profile/subscription/UserSubscription.js (3)
src/components/profile/subscription/SubscribeModal.js (1)
  • subscriptionType (38-38)
src/components/bitcoinConnect/SubscriptionPaymentButton.js (2)
  • getAmount (40-42)
  • SubscriptionPaymentButtons (20-351)
src/pages/api/users/subscription/cron.js (1)
  • getAmount (12-15)
🪛 Biome (1.9.4)
src/components/profile/subscription/UserSubscription.js

[error] 45-45: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 54-54: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (14)
src/pages/about.js (2)

21-21: Good addition of centralized subscription period constants.

Adding the import for SUBSCRIPTION_PERIODS aligns with good software engineering practices by centralizing configuration values.


121-121: Well-implemented refactoring to support multiple subscription types.

Replacing the hardcoded 31 days with SUBSCRIPTION_PERIODS.MONTHLY.DAYS improves maintainability and consistency across the application. This change properly supports the new functionality for different subscription types (monthly and yearly).

src/components/profile/subscription/UserSubscription.js (12)

19-20: Good addition of necessary imports

The imports for SelectButton and the subscription period constants are correctly added to support the new subscription type functionality.


37-42: Well-structured subscription plan implementation

Good implementation of subscription type state and options. The default 'monthly' value ensures backward compatibility with existing users.


47-49: Good initialization from user data

This correctly initializes the subscription type from the user's existing subscription, maintaining state consistency.


81-81: API update to include subscription type

Good update to include the subscription type in the API call, ensuring the backend can properly handle the different subscription durations.


120-123: Consistent price calculation

The getAmount function correctly returns 500 for yearly and 50 for monthly subscriptions, which is consistent with the values used in the relevant code snippets from other files.


168-187: Improved iconography and spacing

Nice adjustments to icon margins to improve visual consistency and readability of the subscription benefits list.


189-214: Well-designed subscription plan selector

The subscription plan selector is well-implemented with:

  • Clear UI for toggling between monthly and yearly plans
  • Visual indication of savings for yearly subscriptions (~17%)
  • Clear price display showing the amount and plan duration

This makes it easy for users to understand their options and the value proposition of each plan.


222-222: Proper integration with payment component

Correctly passing the subscriptionType to the SubscriptionPaymentButtons component ensures that payments are processed with the correct amount and duration.


247-254: Improved subscription information display

Good addition of the current plan type and renewal date information. This helps users understand their subscription status more clearly.


318-333: Clear subscription plan differentiation in FAQ

The added FAQ item about the difference between monthly and yearly plans is helpful for users to understand the value proposition of each option.


338-339: Updated subscription duration information

Good update to clarify that the subscription duration depends on the selected plan (monthly or yearly).


346-346: Clear recurring payment schedule information

Good update to clarify that recurring payments follow the chosen schedule (monthly or yearly).

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a 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 (3)
src/pages/api/users/subscription/cron.js (1)

58-58: Fix inconsistent optional chaining.

There's inconsistent use of optional chaining in the condition check.

-if (response && response?.preimage) {
+if (response?.preimage) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 58-58: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/components/profile/subscription/UserSubscription.js (2)

45-50: Fix inconsistent optional chaining.

There's inconsistent use of optional chaining in this condition.

-if (session && session?.user) {
+if (session?.user) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 45-45: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


54-67: Consider using optional chaining consistently.

The condition checks could be more consistent with optional chaining patterns.

-if (user && user.role) {
+if (user?.role) {

The implementation of dynamic expiration date calculation is good, properly using the shared helper function based on subscription type.

🧰 Tools
🪛 Biome (1.9.4)

[error] 54-54: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between ab7b5fc and 34d0165.

📒 Files selected for processing (3)
  • src/components/bitcoinConnect/SubscriptionPaymentButton.js (12 hunks)
  • src/components/profile/subscription/UserSubscription.js (7 hunks)
  • src/pages/api/users/subscription/cron.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/bitcoinConnect/SubscriptionPaymentButton.js
🧰 Additional context used
🪛 Biome (1.9.4)
src/pages/api/users/subscription/cron.js

[error] 58-58: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/components/profile/subscription/UserSubscription.js

[error] 45-45: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 54-54: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (12)
src/pages/api/users/subscription/cron.js (6)

11-15: Good implementation of the subscription type-based pricing.

The helper function is well-designed, providing clear pricing differentiation between subscription types with appropriate comments about the savings percentage.


20-31: Well-structured statistics tracking.

The implementation properly handles both subscription types with clear comments about different expiration periods and a well-organized statistics object for tracking metrics.


35-38: LGTM! Defaulting to monthly subscription is a good failsafe.

The destructuring with default value for subscriptionType provides backward compatibility for existing users without a specified type.


50-53: Nice dynamic invoice comment formatting.

The code properly formats the subscription type with the first letter capitalized, creating professional-looking invoice comments.


59-64: Good implementation of subscription renewal with type preservation.

The code correctly passes the subscription type to the updateUserSubscription function, maintaining the user's selected plan.


83-92: Well-detailed statistics reporting.

The logging and response JSON provide clear insights into subscription performance metrics broken down by type, which will be valuable for monitoring the effectiveness of the yearly subscription option.

src/components/profile/subscription/UserSubscription.js (6)

19-20: Good implementation of required imports.

The addition of SelectButton and subscription period utilities provides the necessary components for the new subscription type functionality.


37-42: Well-structured subscription options.

The state initialization and options definition for subscription types is clear and follows React best practices.


75-95: Properly updated API call with subscription type.

The subscription success handler correctly includes the selected subscription type in the API request, ensuring proper persisting of the user's choice.


184-209: Excellent subscription plan selector UI.

The subscription plan selector is well-implemented with:

  • Clear visual distinction between plans
  • Informative savings message for yearly subscriptions
  • Price display with appropriate context
  • Good responsive design considerations

The 17% savings message aligns with the actual pricing (500,000 vs 12 × 50,000).


242-249: Good display of current subscription details.

The current plan section clearly shows the subscription type and renewal date, providing users with essential information about their subscription status.


322-328: Clear FAQ about subscription differences.

The FAQ explanation about the difference between monthly and yearly plans is informative and accurately reflects the pricing structure.

@AustinKelsay AustinKelsay merged commit 2291591 into main May 15, 2025
4 checks passed
@AustinKelsay AustinKelsay linked an issue May 19, 2025 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor/subscriptions

1 participant