From 4d0883bef3761027ebcb07f6b7b1351df32d288c Mon Sep 17 00:00:00 2001 From: karthikeyan Date: Mon, 2 Feb 2026 19:19:23 +0530 Subject: [PATCH 1/3] Add revenue and subscription analytics design --- src/flimix/analytics/includes/header.html | 111 ++++++ .../charts_script.html | 319 ++++++++++++++++++ .../revenue_and_subscription/index.html | 28 ++ .../revenue_and_subscription/kpi_cards.html | 113 +++++++ .../revenue_by_plan.html | 11 + .../revenue_trend.html | 11 + .../subscription_lifecycle.html | 101 ++++++ src/flimix/layouts/sidebar_layout.html | 1 + 8 files changed, 695 insertions(+) create mode 100644 src/flimix/analytics/includes/header.html create mode 100644 src/flimix/analytics/revenue_and_subscription/charts_script.html create mode 100644 src/flimix/analytics/revenue_and_subscription/index.html create mode 100644 src/flimix/analytics/revenue_and_subscription/kpi_cards.html create mode 100644 src/flimix/analytics/revenue_and_subscription/revenue_by_plan.html create mode 100644 src/flimix/analytics/revenue_and_subscription/revenue_trend.html create mode 100644 src/flimix/analytics/revenue_and_subscription/subscription_lifecycle.html diff --git a/src/flimix/analytics/includes/header.html b/src/flimix/analytics/includes/header.html new file mode 100644 index 00000000000..920fbbf891d --- /dev/null +++ b/src/flimix/analytics/includes/header.html @@ -0,0 +1,111 @@ +{% set _active = analytics_active_tab | default('revenue_and_subscription') %} + +
+
+
+
+

+ Analytics Dashboard

+
+
+
+ +
+ + + +
+
+ +
+
+
+ +
\ No newline at end of file diff --git a/src/flimix/analytics/revenue_and_subscription/charts_script.html b/src/flimix/analytics/revenue_and_subscription/charts_script.html new file mode 100644 index 00000000000..67d6d153492 --- /dev/null +++ b/src/flimix/analytics/revenue_and_subscription/charts_script.html @@ -0,0 +1,319 @@ + + \ No newline at end of file diff --git a/src/flimix/analytics/revenue_and_subscription/index.html b/src/flimix/analytics/revenue_and_subscription/index.html new file mode 100644 index 00000000000..5e4dcf75e2b --- /dev/null +++ b/src/flimix/analytics/revenue_and_subscription/index.html @@ -0,0 +1,28 @@ +--- +permalink: flimix/analytics/revenue_and_subscription/ +tags: flimix +staff_view: "Analytics" +title: Revenue and Subscription +--- + +{% extends "src/flimix/layouts/sidebar_layout.html" %} +{% block main_class %}bg-stone-50{% endblock %} + +{% block main_content %} +{% include "src/flimix/analytics/includes/header.html" %} + +
+ {% include "./kpi_cards.html" %} +
+ {% include "./revenue_trend.html" %} + {% include "./revenue_by_plan.html" %} +
+
+ {% include "./subscription_lifecycle.html" %} +
+
+ +{% endblock main_content %} +{% block script %} +{% include "./charts_script.html" %} +{% endblock script %} \ No newline at end of file diff --git a/src/flimix/analytics/revenue_and_subscription/kpi_cards.html b/src/flimix/analytics/revenue_and_subscription/kpi_cards.html new file mode 100644 index 00000000000..6b760713a35 --- /dev/null +++ b/src/flimix/analytics/revenue_and_subscription/kpi_cards.html @@ -0,0 +1,113 @@ +
+
+
+ + + + +
+

Total Revenue

+

₹15,45,000

+
+
+
+ vs previous period + + + + + + +15% + +
+
+ +
+
+ + + + + + +
+

Active Subscribers

+

12,450

+
+
+
+ 450 new in period + + + + + + +3% + +
+
+ +
+
+ + + + +
+

Net Growth

+

+330

+
+
+
+ New − Cancelled + + + + + + +2.7% + +
+
+ +
+
+ + + + + + +
+

MRR

+

₹8,20,000

+
+
+
+ Monthly recurring + + + + + + +8% + +
+
+
\ No newline at end of file diff --git a/src/flimix/analytics/revenue_and_subscription/revenue_by_plan.html b/src/flimix/analytics/revenue_and_subscription/revenue_by_plan.html new file mode 100644 index 00000000000..e40d50b1f55 --- /dev/null +++ b/src/flimix/analytics/revenue_and_subscription/revenue_by_plan.html @@ -0,0 +1,11 @@ +
+
+
+

Revenue by Plan

+

Distribution of revenue across subscription plans

+
+ View breakdown +
+
+
\ No newline at end of file diff --git a/src/flimix/analytics/revenue_and_subscription/revenue_trend.html b/src/flimix/analytics/revenue_and_subscription/revenue_trend.html new file mode 100644 index 00000000000..276ff405ee3 --- /dev/null +++ b/src/flimix/analytics/revenue_and_subscription/revenue_trend.html @@ -0,0 +1,11 @@ +
+
+
+

Revenue Trend

+

Subscription vs pay-per-view (TVOD) revenue over time

+
+ View breakdown +
+
+
\ No newline at end of file diff --git a/src/flimix/analytics/revenue_and_subscription/subscription_lifecycle.html b/src/flimix/analytics/revenue_and_subscription/subscription_lifecycle.html new file mode 100644 index 00000000000..77aafc95734 --- /dev/null +++ b/src/flimix/analytics/revenue_and_subscription/subscription_lifecycle.html @@ -0,0 +1,101 @@ + +
+ +
+
+
+

Subscription Lifecycle

+

Churn, cancellations, upgrades and downgrades + over the period

+
+ View breakdown + + + + +
+
+ + + +
+
+
+
+
+
+ +
+
+

Breakdown

+ +
    +
  • +
    +
    + + New +
    +
    +
    + 450 +
    +
  • +
  • +
    +
    + + Renewal +
    +
    +
    + 2,650 +
    +
  • +
  • +
    +
    + + Upgraded +
    +
    +
    + 139 +
    +
  • +
  • +
    +
    + + Downgraded +
    +
    +
    + 102 +
    +
  • +
  • +
    +
    + + Cancelled +
    +
    +
    + 120 +
    +
  • +
+
+
+
+
\ No newline at end of file diff --git a/src/flimix/layouts/sidebar_layout.html b/src/flimix/layouts/sidebar_layout.html index 59a7ce4b836..ca015cf6301 100644 --- a/src/flimix/layouts/sidebar_layout.html +++ b/src/flimix/layouts/sidebar_layout.html @@ -9,6 +9,7 @@ {% include "src/flimix/partials/desktop_sidebar.html" %}
{% include "src/flimix/partials/header.html" %} +
{% block main_content %} From 85a76c02f241755c9d5d94d97046e4228881e12a Mon Sep 17 00:00:00 2001 From: karthikeyan Date: Mon, 9 Feb 2026 18:22:39 +0530 Subject: [PATCH 2/3] Add UI for User Analytics --- .../user_analytics/active_users_trend.html | 9 + .../user_analytics/charts_script.html | 457 ++++++++++++++++++ .../analytics/user_analytics/index.html | 31 ++ .../analytics/user_analytics/kpi_cards.html | 145 ++++++ .../user_analytics/retention_cohort.html | 40 ++ .../user_analytics/watch_time_sessions.html | 9 + 6 files changed, 691 insertions(+) create mode 100644 src/flimix/analytics/user_analytics/active_users_trend.html create mode 100644 src/flimix/analytics/user_analytics/charts_script.html create mode 100644 src/flimix/analytics/user_analytics/index.html create mode 100644 src/flimix/analytics/user_analytics/kpi_cards.html create mode 100644 src/flimix/analytics/user_analytics/retention_cohort.html create mode 100644 src/flimix/analytics/user_analytics/watch_time_sessions.html diff --git a/src/flimix/analytics/user_analytics/active_users_trend.html b/src/flimix/analytics/user_analytics/active_users_trend.html new file mode 100644 index 00000000000..bfce4509838 --- /dev/null +++ b/src/flimix/analytics/user_analytics/active_users_trend.html @@ -0,0 +1,9 @@ +
+
+
+

Active Users over time

+
+
+
+
+ diff --git a/src/flimix/analytics/user_analytics/charts_script.html b/src/flimix/analytics/user_analytics/charts_script.html new file mode 100644 index 00000000000..45388292ff9 --- /dev/null +++ b/src/flimix/analytics/user_analytics/charts_script.html @@ -0,0 +1,457 @@ + + + diff --git a/src/flimix/analytics/user_analytics/index.html b/src/flimix/analytics/user_analytics/index.html new file mode 100644 index 00000000000..24b17e615b8 --- /dev/null +++ b/src/flimix/analytics/user_analytics/index.html @@ -0,0 +1,31 @@ +--- +permalink: flimix/analytics/user_analytics/ +tags: flimix +staff_view: "Analytics" +title: User Analytics +--- + +{% extends "src/flimix/layouts/sidebar_layout.html" %} +{% block main_class %}bg-stone-50{% endblock %} + +{% block main_content %} +{% set analytics_active_tab = 'user_analytics' %} +{% include "src/flimix/analytics/includes/header.html" %} + +
+ {% include "./kpi_cards.html" %} +
+ {% include "./active_users_trend.html" %} + {% include "./watch_time_sessions.html" %} +
+
+ {% include "./retention_cohort.html" %} +
+
+ +{% endblock main_content %} + +{% block script %} +{% include "./charts_script.html" %} +{% endblock script %} + diff --git a/src/flimix/analytics/user_analytics/kpi_cards.html b/src/flimix/analytics/user_analytics/kpi_cards.html new file mode 100644 index 00000000000..7dc7c1fbff9 --- /dev/null +++ b/src/flimix/analytics/user_analytics/kpi_cards.html @@ -0,0 +1,145 @@ +
+ +
+
+ + + + + +
+

Total Users

+

1,52,340

+
+
+
+ vs previous period + + + + + + +4.2% + +
+
+ + +
+
+ + + + + + +
+

New Users

+

8,420

+
+
+
+ in this period + + + + + + +8.1% + +
+
+ + +
+
+ + + + + +
+
+

+ Active Users +

+
+ +
+
+

1,02,300

+
+
+
+ 67% of total users + + + + + + +3.5% + +
+
+ + +
+
+ + + + +
+

Avg watch time / active user

+

42 min

+
+
+
+ vs previous period + + + + + + +6.8% + +
+
+
+ diff --git a/src/flimix/analytics/user_analytics/retention_cohort.html b/src/flimix/analytics/user_analytics/retention_cohort.html new file mode 100644 index 00000000000..5cee8b93055 --- /dev/null +++ b/src/flimix/analytics/user_analytics/retention_cohort.html @@ -0,0 +1,40 @@ +
+
+
+

Retention & cohort

+

+ Track how consistently users return after signup. +

+
+ + +
+ + + + +
+ + Very low + +
+ + + + + +
+ + High retention + +
+
+
+
+
diff --git a/src/flimix/analytics/user_analytics/watch_time_sessions.html b/src/flimix/analytics/user_analytics/watch_time_sessions.html new file mode 100644 index 00000000000..725a5e7aaa4 --- /dev/null +++ b/src/flimix/analytics/user_analytics/watch_time_sessions.html @@ -0,0 +1,9 @@ +
+
+
+

Watch time & sessions over time

+
+
+
+
+ From adeb4f4e1822c1f0b596b25d7d1b46c3c3276459 Mon Sep 17 00:00:00 2001 From: karthikeyan Date: Mon, 9 Feb 2026 18:31:23 +0530 Subject: [PATCH 3/3] progress --- .../user_analytics/charts_script.html | 332 ++++++++++++++---- 1 file changed, 260 insertions(+), 72 deletions(-) diff --git a/src/flimix/analytics/user_analytics/charts_script.html b/src/flimix/analytics/user_analytics/charts_script.html index 45388292ff9..8f71efb542d 100644 --- a/src/flimix/analytics/user_analytics/charts_script.html +++ b/src/flimix/analytics/user_analytics/charts_script.html @@ -82,79 +82,267 @@ }; } - function getRetentionData() { - // Only support "Last 7 days" view for now. - // X-axis: Day 0–Day 6 (days after signup, cumulative). - // Y-axis: daily signup cohorts (newest on top). - // Color: retention % (y), Cell text & tooltip: active user count (active). - - const xCategories = ['Day 0', 'Day 1', 'Day 2', 'Day 3', 'Day 4', 'Day 5', 'Day 6']; - - // Mock data shaped so that newer cohorts have fewer observed days. - const cohorts = [ - { - // Newest signup cohort – only Day 0 data is known. - label: 'Feb 9', - size: 220, - percents: [100, null, null, null, null, null, null], - actives: [220, null, null, null, null, null, null] - }, - { - label: 'Feb 8', - size: 240, - percents: [100, 68, null, null, null, null, null], - actives: [240, 163, null, null, null, null, null] - }, - { - label: 'Feb 7', - size: 260, - percents: [100, 66, 58, null, null, null, null], - actives: [260, 172, 151, null, null, null, null] - }, - { - label: 'Feb 6', - size: 280, - percents: [100, 64, 56, 50, null, null, null], - actives: [280, 179, 157, 140, null, null, null] - }, - { - label: 'Feb 5', - size: 300, - percents: [100, 62, 54, 48, 43, null, null], - actives: [300, 186, 162, 144, 129, null, null] - }, - { - label: 'Feb 4', - size: 320, - percents: [100, 60, 52, 46, 41, 37, null], - actives: [320, 192, 166, 147, 131, 118, null] - }, - { - // Oldest cohort – has full 7-day observation window. - label: 'Feb 3', - size: 340, - percents: [100, 58, 50, 44, 40, 36, 32], - actives: [340, 197, 170, 150, 136, 122, 109] - } - ]; - - // Newest cohort first so it appears at the top of the heatmap. - const series = cohorts.map(cohort => ({ - name: cohort.label, - data: xCategories.map((xLabel, idx) => ({ - x: xLabel, - y: cohort.percents[idx] ?? null, - active: cohort.actives[idx] ?? null - })) - })); + function getRetentionData(range) { + const rangeKey = range || (dateRangeSelect ? dateRangeSelect.value : '30'); - return { - rangeKey: '7', - valueType: 'percent', - chartHeight: 350, - xCategories, - series - }; + // Last 7 days – daily signup cohorts + if (rangeKey === '7') { + const xCategories = ['Day 0', 'Day 1', 'Day 2', 'Day 3', 'Day 4', 'Day 5', 'Day 6']; + + const cohorts7 = [ + { + // Newest signup cohort – only Day 0 data is known. + label: 'Feb 9', + size: 220, + percents: [100, null, null, null, null, null, null], + actives: [220, null, null, null, null, null, null] + }, + { + label: 'Feb 8', + size: 240, + percents: [100, 68, null, null, null, null, null], + actives: [240, 163, null, null, null, null, null] + }, + { + label: 'Feb 7', + size: 260, + percents: [100, 66, 58, null, null, null, null], + actives: [260, 172, 151, null, null, null, null] + }, + { + label: 'Feb 6', + size: 280, + percents: [100, 64, 56, 50, null, null, null], + actives: [280, 179, 157, 140, null, null, null] + }, + { + label: 'Feb 5', + size: 300, + percents: [100, 62, 54, 48, 43, null, null], + actives: [300, 186, 162, 144, 129, null, null] + }, + { + label: 'Feb 4', + size: 320, + percents: [100, 60, 52, 46, 41, 37, null], + actives: [320, 192, 166, 147, 131, 118, null] + }, + { + // Oldest cohort – has full 7-day observation window. + label: 'Feb 3', + size: 340, + percents: [100, 58, 50, 44, 40, 36, 32], + actives: [340, 197, 170, 150, 136, 122, 109] + } + ]; + + const series7 = cohorts7.map(cohort => ({ + name: cohort.label, + data: xCategories.map((xLabel, idx) => ({ + x: xLabel, + y: cohort.percents[idx] ?? null, + active: cohort.actives[idx] ?? null + })) + })); + + return { + rangeKey: '7', + valueType: 'percent', + chartHeight: 350, + xCategories, + series: series7 + }; + } + + // Last 30 days – weekly signup cohorts, cumulative retention + if (rangeKey === '30') { + const xCategories = ['Day 0', 'Day 1', 'Day 7', 'Day 14', 'Day 30']; + + const cohorts30 = [ + { + // Latest week – only Day 0 & Day 1 known so far + label: 'Feb 3–9', + size: 420, + percents: [100, 62, null, null, null], + actives: [420, 260, null, null, null] + }, + { + label: 'Jan 27–Feb 2', + size: 440, + percents: [100, 60, 74, null, null], + actives: [440, 264, 326, null, null] + }, + { + label: 'Jan 20–26', + size: 460, + percents: [100, 58, 72, 80, null], + actives: [460, 267, 331, 368, null] + }, + { + // Oldest visible cohort – full 30‑day window + label: 'Jan 13–19', + size: 480, + percents: [100, 55, 70, 78, 86], + actives: [480, 264, 336, 374, 413] + } + ]; + + const series30 = cohorts30.map(cohort => ({ + name: cohort.label, + data: xCategories.map((xLabel, idx) => ({ + x: xLabel, + y: cohort.percents[idx] ?? null, + active: cohort.actives[idx] ?? null + })) + })); + + return { + rangeKey: '30', + valueType: 'percent', + chartHeight: 350, + xCategories, + series: series30 + }; + } + + // Last 90 days – weekly signup cohorts, cumulative retention by week + if (rangeKey === '90') { + const xCategories = ['Week 0', 'Week 1', 'Week 2', 'Week 4', 'Week 8', 'Week 12']; + + const cohorts90 = [ + { label: 'Feb 3–9', size: 380, percents: [100, null, null, null, null, null], actives: [380, null, null, null, null, null] }, + { label: 'Jan 27–Feb 2', size: 400, percents: [100, 68, null, null, null, null], actives: [400, 272, null, null, null, null] }, + { label: 'Jan 20–26', size: 410, percents: [100, 66, 72, null, null, null], actives: [410, 271, 295, null, null, null] }, + { label: 'Jan 13–19', size: 420, percents: [100, 65, 70, 76, null, null], actives: [420, 273, 294, 319, null, null] }, + { label: 'Jan 6–12', size: 430, percents: [100, 64, 69, 74, 68, null], actives: [430, 275, 297, 318, 292, null] }, + { label: 'Dec 30–Jan 5', size: 440, percents: [100, 63, 68, 73, 67, 62], actives: [440, 277, 299, 321, 295, 273] }, + { label: 'Dec 23–29', size: 450, percents: [100, 62, 67, 72, 66, 61], actives: [450, 279, 302, 324, 297, 274] }, + { label: 'Dec 16–22', size: 460, percents: [100, 61, 66, 71, 65, 60], actives: [460, 281, 304, 327, 299, 276] }, + { label: 'Dec 9–15', size: 470, percents: [100, 60, 65, 70, 64, 59], actives: [470, 282, 306, 329, 301, 277] }, + { label: 'Dec 2–8', size: 480, percents: [100, 59, 64, 69, 63, 58], actives: [480, 283, 307, 331, 302, 278] }, + { label: 'Nov 25–Dec 1', size: 490, percents: [100, 58, 63, 68, 62, 57], actives: [490, 284, 309, 333, 304, 279] }, + { label: 'Nov 18–24', size: 500, percents: [100, 57, 62, 67, 61, 56], actives: [500, 285, 310, 335, 305, 280] }, + { label: 'Nov 11–17', size: 510, percents: [100, 56, 61, 66, 60, 55], actives: [510, 286, 311, 337, 306, 281] } + ]; + + const series90 = cohorts90.map(cohort => ({ + name: cohort.label, + data: xCategories.map((xLabel, idx) => ({ + x: xLabel, + y: cohort.percents[idx] ?? null, + active: cohort.actives[idx] ?? null + })) + })); + + return { + rangeKey: '90', + valueType: 'percent', + chartHeight: 420, + xCategories, + series: series90 + }; + } + + // Last 12 months – monthly signup cohorts, cumulative retention by month + if (rangeKey === '365') { + const xCategories = ['Month 0', 'Month 1', 'Month 3', 'Month 6', 'Month 12']; + + const cohorts365 = [ + { + // Latest month – only Month 0 known so far + label: 'Feb 2026', + size: 5200, + percents: [100, null, null, null, null], + actives: [5200, null, null, null, null] + }, + { + label: 'Jan 2026', + size: 5100, + percents: [100, 68, null, null, null], + actives: [5100, 3468, null, null, null] + }, + { + label: 'Dec 2025', + size: 5000, + percents: [100, 66, 74, null, null], + actives: [5000, 3300, 3700, null, null] + }, + { + label: 'Nov 2025', + size: 4900, + percents: [100, 64, 72, 80, null], + actives: [4900, 3136, 3528, 3920, null] + }, + { + label: 'Oct 2025', + size: 4800, + percents: [100, 62, 70, 78, 86], + actives: [4800, 2976, 3360, 3744, 4128] + }, + { + label: 'Sep 2025', + size: 4700, + percents: [100, 60, 68, 76, 84], + actives: [4700, 2820, 3196, 3572, 3948] + }, + { + label: 'Aug 2025', + size: 4600, + percents: [100, 59, 66, 74, 82], + actives: [4600, 2714, 3036, 3404, 3772] + }, + { + label: 'Jul 2025', + size: 4500, + percents: [100, 58, 65, 72, 80], + actives: [4500, 2610, 2925, 3240, 3600] + }, + { + label: 'Jun 2025', + size: 4400, + percents: [100, 57, 64, 71, 79], + actives: [4400, 2508, 2816, 3124, 3476] + }, + { + label: 'May 2025', + size: 4300, + percents: [100, 56, 63, 70, 78], + actives: [4300, 2408, 2709, 3010, 3354] + }, + { + label: 'Apr 2025', + size: 4200, + percents: [100, 55, 62, 69, 77], + actives: [4200, 2310, 2604, 2898, 3234] + }, + { + label: 'Mar 2025', + size: 4100, + percents: [100, 54, 61, 68, 76], + actives: [4100, 2214, 2501, 2788, 3116] + } + ]; + + const series365 = cohorts365.map(cohort => ({ + name: cohort.label, + data: xCategories.map((xLabel, idx) => ({ + x: xLabel, + y: cohort.percents[idx] ?? null, + active: cohort.actives[idx] ?? null + })) + })); + + return { + rangeKey: '365', + valueType: 'percent', + chartHeight: 440, + xCategories, + series: series365 + }; + } + + // Fallback: use 30‑day config + return getRetentionData('30'); } function renderRetentionChart(range) {