File size: 16,852 Bytes
bb6d7b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
"""
Subscription management component.

This component provides UI for managing subscription plans.
"""
import os
import streamlit as st
import pandas as pd
from datetime import datetime
import json

import stripe
from streamlit_extras.colored_header import colored_header
from streamlit_extras.metric_cards import style_metric_cards

from src.streamlit_subscription_services import (
    get_subscription_plans_df,
    get_subscription_plan,
    get_user_current_subscription,
    subscribe_user_to_plan,
    cancel_subscription,
    initialize_default_plans
)

# Set up Stripe publishable key for client-side usage
STRIPE_PUBLISHABLE_KEY = os.environ.get("STRIPE_PUBLISHABLE_KEY")


def format_price(price):
    """Format price display."""
    if price == 0:
        return "Free"
    return f"${price:.2f}"


def render_pricing_card(plan, selected_period="monthly"):
    """Render a pricing card for a subscription plan."""
    plan_id = plan["id"]
    plan_name = plan["name"]
    plan_tier = plan["tier"]
    description = plan["description"]
    
    # Determine price based on selected period
    if selected_period == "monthly":
        price = plan["price_monthly"]
        period_text = "per month"
        billing_term = "monthly"
    else:
        price = plan["price_annually"]
        period_text = "per year"
        billing_term = "annually"
    
    # Format price for display
    price_display = format_price(price)
    
    # Feature list
    features = [
        f"βœ“ {plan['max_alerts'] if plan['max_alerts'] > 0 else 'Unlimited'} alerts",
        f"βœ“ {plan['max_reports'] if plan['max_reports'] > 0 else 'Unlimited'} reports",
        f"βœ“ {plan['max_searches_per_day'] if plan['max_searches_per_day'] > 0 else 'Unlimited'} searches per day",
        f"βœ“ {plan['max_monitoring_keywords'] if plan['max_monitoring_keywords'] > 0 else 'Unlimited'} monitoring keywords",
        f"βœ“ {plan['max_data_retention_days']} days data retention"
    ]
    
    if plan["supports_api_access"]:
        features.append("βœ“ API access")
    
    if plan["supports_live_feed"]:
        features.append("βœ“ Live feed")
    
    if plan["supports_dark_web_monitoring"]:
        features.append("βœ“ Dark web monitoring")
    
    if plan["supports_export"]:
        features.append("βœ“ Data export")
    
    if plan["supports_advanced_analytics"]:
        features.append("βœ“ Advanced analytics")
    
    # Card style based on tier
    if plan_tier == "free":
        border_color = "#3498db"  # Blue
        header_color = "#3498db"
    elif plan_tier == "basic":
        border_color = "#2ecc71"  # Green
        header_color = "#2ecc71"
    elif plan_tier == "professional":
        border_color = "#f39c12"  # Orange
        header_color = "#f39c12"
    else:  # Enterprise
        border_color = "#9b59b6"  # Purple
        header_color = "#9b59b6"
    
    # Render card
    st.markdown(f"""
    <div style="border: 2px solid {border_color}; border-radius: 10px; padding: 20px; height: 100%;">
        <h3 style="color: {header_color}; text-align: center;">{plan_name}</h3>
        <h2 style="text-align: center; margin-top: 10px; margin-bottom: 5px;">{price_display}</h2>
        <p style="text-align: center; color: #999; margin-bottom: 20px;">{period_text}</p>
        <p style="text-align: center; margin-bottom: 20px;">{description}</p>
        <div style="margin-bottom: 20px;">
            {"<br>".join([f'<div style="margin-bottom: 8px;">{feature}</div>' for feature in features])}
        </div>
    </div>
    """, unsafe_allow_html=True)
    
    # Subscribe button
    if st.button(f"Choose {plan_name}", key=f"choose_{plan_id}_{selected_period}"):
        if not plan_tier == "free" and STRIPE_PUBLISHABLE_KEY:
            st.session_state.show_payment_form = True
            st.session_state.selected_plan = plan
            st.session_state.selected_billing_period = billing_term
        else:
            # Free plan - no payment needed
            # Assume user ID 1 for demonstration
            user_id = 1
            subscription = subscribe_user_to_plan(
                user_id=user_id,
                plan_id=plan_id,
                billing_period=billing_term,
                create_stripe_subscription=False
            )
            
            if subscription:
                st.success(f"You're now subscribed to the {plan_name} plan!")
                st.session_state.current_user_subscription = subscription
            else:
                st.error("Failed to subscribe. Please try again.")
            
            st.rerun()


def render_payment_form():
    """Render the payment form for subscription."""
    if not STRIPE_PUBLISHABLE_KEY:
        st.error("Stripe API key is not configured. Payment processing is unavailable.")
        return
    
    plan = st.session_state.selected_plan
    billing_period = st.session_state.selected_billing_period
    
    st.markdown("### Payment Information")
    
    # Calculate amount based on billing period
    if billing_period == "monthly":
        amount = plan["price_monthly"]
    else:
        amount = plan["price_annually"]
    
    st.markdown(f"You're subscribing to the **{plan['name']} plan** ({billing_period}) for **{format_price(amount)}**.")
    
    # Name and email inputs
    col1, col2 = st.columns(2)
    
    with col1:
        name = st.text_input("Full Name")
    
    with col2:
        email = st.text_input("Email Address")
    
    # HTML/JS for Stripe Elements
    st.markdown("""
    <div id="card-element" style="padding: 10px; border: 1px solid #ccc; border-radius: 4px; margin-bottom: 20px;"></div>
    <div id="card-errors" style="color: #e74c3c; margin-bottom: 20px;"></div>
    
    <script src="https://js.stripe.com/v3/"></script>
    <script type="text/javascript">
        // Initialize Stripe with publishable key
        var stripe = Stripe('%s');
        var elements = stripe.elements();
        
        // Create card element
        var card = elements.create('card');
        card.mount('#card-element');
        
        // Handle real-time validation errors
        card.addEventListener('change', function(event) {
            var displayError = document.getElementById('card-errors');
            if (event.error) {
                displayError.textContent = event.error.message;
            } else {
                displayError.textContent = '';
            }
        });
        
        // Set up payment method creation
        window.createPaymentMethod = function() {
            stripe.createPaymentMethod({
                type: 'card',
                card: card,
                billing_details: {
                    name: '%s',
                    email: '%s'
                }
            }).then(function(result) {
                if (result.error) {
                    var errorElement = document.getElementById('card-errors');
                    errorElement.textContent = result.error.message;
                } else {
                    // Send payment method ID to Streamlit
                    window.parent.postMessage({
                        type: 'payment-method-created',
                        paymentMethodId: result.paymentMethod.id
                    }, '*');
                }
            });
        }
    </script>
    """ % (STRIPE_PUBLISHABLE_KEY, name, email), unsafe_allow_html=True)
    
    # Submit button
    if st.button("Subscribe Now", key="subscribe_button"):
        # Call JavaScript function to create payment method
        st.markdown("""
        <script>
            window.createPaymentMethod();
        </script>
        """, unsafe_allow_html=True)
        
        # In a real implementation, we would need to handle the callback from Stripe
        # For now, simulate success for demonstration
        payment_method_id = "pm_" + "".join([str(i) for i in range(24)])  # Fake payment method ID
        
        # Subscribe user with payment method
        # Assume user ID 1 for demonstration
        user_id = 1
        subscription = subscribe_user_to_plan(
            user_id=user_id,
            plan_id=plan["id"],
            billing_period=billing_period,
            create_stripe_subscription=True,
            payment_method_id=payment_method_id
        )
        
        if subscription:
            st.success(f"You're now subscribed to the {plan['name']} plan!")
            st.session_state.show_payment_form = False
            st.session_state.current_user_subscription = subscription
        else:
            st.error("Failed to subscribe. Please try again.")
        
        st.rerun()
    
    # Cancel button
    if st.button("Cancel", key="cancel_payment"):
        st.session_state.show_payment_form = False
        st.rerun()


def render_subscription_dashboard(user_id=1):
    """Render the subscription dashboard for the current user."""
    # Get current subscription
    subscription = get_user_current_subscription(user_id)
    st.session_state.current_user_subscription = subscription
    
    if subscription:
        plan_tier = subscription.get("plan_tier", "").capitalize()
        plan_name = subscription.get("plan_name", "Unknown Plan")
        status = subscription.get("status", "").capitalize()
        billing_period = subscription.get("billing_period", "").capitalize()
        
        period_start = subscription.get("current_period_start")
        period_end = subscription.get("current_period_end")
        
        # Format dates
        start_date = period_start.strftime("%B %d, %Y") if period_start else "N/A"
        end_date = period_end.strftime("%B %d, %Y") if period_end else "N/A"
        
        st.markdown(f"### Current Subscription: {plan_name}")
        
        col1, col2, col3 = st.columns(3)
        
        with col1:
            st.metric("Status", status)
        
        with col2:
            st.metric("Billing Period", billing_period)
        
        with col3:
            days_left = (period_end - datetime.now()).days if period_end else 0
            days_left = max(0, days_left)
            st.metric("Days Remaining", days_left)
        
        style_metric_cards()
        
        st.markdown(f"""
        <div style="margin-top: 20px; padding: 15px; background-color: rgba(44, 62, 80, 0.2); border-radius: 6px;">
            <p><strong>Billing Period:</strong> {start_date} to {end_date}</p>
        </div>
        """, unsafe_allow_html=True)
        
        # Cancel subscription button
        if status.lower() != "canceled":
            if st.button("Cancel Subscription", key="cancel_subscription"):
                if cancel_subscription(subscription["id"]):
                    st.success("Your subscription has been canceled. You'll still have access until the end of your billing period.")
                    st.rerun()
                else:
                    st.error("Failed to cancel subscription. Please try again.")
    
    # View other plans button
    if st.button("View Available Plans", key="view_plans"):
        st.session_state.show_pricing_table = True
        st.rerun()


def render_subscription_metrics(user_id=1):
    """Render subscription usage metrics for the current user."""
    # Get current subscription
    subscription = st.session_state.get("current_user_subscription") or get_user_current_subscription(user_id)
    
    if not subscription:
        return
    
    # Get subscription plan
    plan = get_subscription_plan(subscription["plan_id"])
    
    if not plan:
        return
    
    st.markdown("### Usage Metrics")
    
    # Create metrics
    col1, col2 = st.columns(2)
    col3, col4 = st.columns(2)
    
    with col1:
        max_alerts = plan["max_alerts"]
        alerts_used = 3  # Placeholder value
        alerts_percent = (alerts_used / max_alerts * 100) if max_alerts > 0 else 0
        st.metric("Alerts", f"{alerts_used}/{max_alerts if max_alerts > 0 else '∞'}")
        st.progress(min(alerts_percent, 100) / 100)
    
    with col2:
        max_reports = plan["max_reports"]
        reports_used = 1  # Placeholder value
        reports_percent = (reports_used / max_reports * 100) if max_reports > 0 else 0
        st.metric("Reports", f"{reports_used}/{max_reports if max_reports > 0 else '∞'}")
        st.progress(min(reports_percent, 100) / 100)
    
    with col3:
        max_searches = plan["max_searches_per_day"]
        searches_used = 8  # Placeholder value
        searches_percent = (searches_used / max_searches * 100) if max_searches > 0 else 0
        st.metric("Daily Searches", f"{searches_used}/{max_searches if max_searches > 0 else '∞'}")
        st.progress(min(searches_percent, 100) / 100)
    
    with col4:
        max_keywords = plan["max_monitoring_keywords"]
        keywords_used = 4  # Placeholder value
        keywords_percent = (keywords_used / max_keywords * 100) if max_keywords > 0 else 0
        st.metric("Monitoring Keywords", f"{keywords_used}/{max_keywords if max_keywords > 0 else '∞'}")
        st.progress(min(keywords_percent, 100) / 100)
    
    # List other features
    st.markdown("### Features")
    
    features = []
    
    if plan["supports_api_access"]:
        features.append("βœ“ API Access")
    else:
        features.append("βœ— API Access")
    
    if plan["supports_live_feed"]:
        features.append("βœ“ Live Feed")
    else:
        features.append("βœ— Live Feed")
    
    if plan["supports_dark_web_monitoring"]:
        features.append("βœ“ Dark Web Monitoring")
    else:
        features.append("βœ— Dark Web Monitoring")
    
    if plan["supports_export"]:
        features.append("βœ“ Data Export")
    else:
        features.append("βœ— Data Export")
    
    if plan["supports_advanced_analytics"]:
        features.append("βœ“ Advanced Analytics")
    else:
        features.append("βœ— Advanced Analytics")
    
    # Display features
    cols = st.columns(len(features))
    for i, feature in enumerate(features):
        with cols[i]:
            if feature.startswith("βœ“"):
                st.markdown(f'<div style="text-align: center; color: #2ecc71; font-weight: bold;">{feature}</div>', unsafe_allow_html=True)
            else:
                st.markdown(f'<div style="text-align: center; color: #e74c3c; font-weight: bold;">{feature}</div>', unsafe_allow_html=True)


def render_pricing_table():
    """Render a pricing table with all subscription plans."""
    st.markdown("## Subscription Plans")
    
    # Billing period toggle
    selected_period = st.radio(
        "Billing Period",
        ["monthly", "annually"],
        format_func=lambda x: x.capitalize(),
        horizontal=True
    )
    
    # Note about annual savings
    if selected_period == "annually":
        st.info("Save up to 20% with annual billing")
    
    # Get subscription plans
    plans_df = get_subscription_plans_df()
    
    if plans_df.empty:
        st.warning("No subscription plans available.")
        return
    
    # Convert DataFrame to list of dictionaries
    plans = plans_df.to_dict("records")
    
    # Create a column for each plan
    cols = st.columns(len(plans))
    
    # Render pricing cards
    for i, plan in enumerate(plans):
        with cols[i]:
            render_pricing_card(plan, selected_period)
    
    # Close button
    if st.button("Back to Dashboard", key="close_pricing"):
        st.session_state.show_pricing_table = False
        st.rerun()


def render_subscriptions():
    """
    Main function to render the subscription management component.
    """
    colored_header(
        label="Subscription Management",
        description="Manage your subscription and billing",
        color_name="violet-70"
    )
    
    # Initialize default plans if needed
    initialize_default_plans()
    
    # Initialize session state
    if "show_pricing_table" not in st.session_state:
        st.session_state.show_pricing_table = False
    
    if "show_payment_form" not in st.session_state:
        st.session_state.show_payment_form = False
    
    if "selected_plan" not in st.session_state:
        st.session_state.selected_plan = None
    
    if "selected_billing_period" not in st.session_state:
        st.session_state.selected_billing_period = "monthly"
    
    if "current_user_subscription" not in st.session_state:
        st.session_state.current_user_subscription = None
    
    # Check if we need to show payment form
    if st.session_state.show_payment_form:
        render_payment_form()
        return
    
    # Check if we need to show pricing table
    if st.session_state.show_pricing_table:
        render_pricing_table()
        return
    
    # Render current subscription status and dashboard
    render_subscription_dashboard()
    
    # Render subscription metrics
    render_subscription_metrics()