From 7b87a57cbe462d491b2fd5a83b92ba6ba327254a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Fri, 9 Jan 2026 22:13:20 +0100 Subject: [PATCH 1/7] fix: prompt card shadows on light mode --- apps/start/src/components/organization/prompt-card.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/start/src/components/organization/prompt-card.tsx b/apps/start/src/components/organization/prompt-card.tsx index 97a49d48..8c9111a0 100644 --- a/apps/start/src/components/organization/prompt-card.tsx +++ b/apps/start/src/components/organization/prompt-card.tsx @@ -33,7 +33,7 @@ export function PromptCard({ }} className="fixed bottom-0 right-0 z-50 p-4 max-w-sm" > -
+
Date: Mon, 12 Jan 2026 16:35:32 +0100 Subject: [PATCH 2/7] fix: handle past_due and unpaid from polar --- apps/api/src/controllers/webhook.controller.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/api/src/controllers/webhook.controller.ts b/apps/api/src/controllers/webhook.controller.ts index ccd447a2..788e2741 100644 --- a/apps/api/src/controllers/webhook.controller.ts +++ b/apps/api/src/controllers/webhook.controller.ts @@ -191,7 +191,9 @@ export async function polarWebhook( where: { subscriptionCustomerId: event.data.customer.id, subscriptionId: event.data.id, - subscriptionStatus: 'active', + subscriptionStatus: { + in: ['active', 'past_due', 'unpaid'], + }, }, }); From ca15717885322ff7cd21ab3321a115617257d133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Wed, 7 Jan 2026 10:04:21 +0100 Subject: [PATCH 3/7] wip --- apps/justfuckinguseopenpanel/favicon.ico | Bin 0 -> 15086 bytes apps/justfuckinguseopenpanel/index.html | 505 +++++++++++ apps/justfuckinguseopenpanel/ogimage.png | Bin 0 -> 63995 bytes .../screenshots/dashboard-dark.webp | Bin 0 -> 38112 bytes .../screenshots/overview-dark.webp | Bin 0 -> 47014 bytes .../screenshots/profile-dark.webp | Bin 0 -> 40038 bytes .../screenshots/realtime-dark.webp | Bin 0 -> 47506 bytes .../screenshots/report-dark.webp | Bin 0 -> 48014 bytes apps/justfuckinguseopenpanel/wrangler.jsonc | 7 + .../src/components/report-chart/index.tsx | 3 + .../components/report-chart/report-editor.tsx | 2 - .../components/report-chart/sankey/chart.tsx | 302 +++++++ .../components/report-chart/sankey/index.tsx | 93 +++ .../src/components/report/ReportChartType.tsx | 2 + .../src/components/report/reportSlice.ts | 80 +- .../report/sidebar/ReportSeries.tsx | 148 ++-- .../report/sidebar/ReportSeriesItem.tsx | 114 +++ .../report/sidebar/ReportSettings.tsx | 102 ++- .../report/sidebar/ReportSidebar.tsx | 16 +- .../report/sidebar/report-fixed-events.tsx | 223 +++++ .../src/components/ui/combobox-events.tsx | 2 +- ....$organizationId.$projectId.dashboards.tsx | 2 + ...zationId.$projectId.reports_.$reportId.tsx | 1 + packages/constants/index.ts | 1 + packages/db/index.ts | 1 + .../migration.sql | 5 + packages/db/prisma/schema.prisma | 3 + packages/db/src/clickhouse/query-builder.ts | 7 + packages/db/src/services/reports.service.ts | 16 +- packages/db/src/services/sankey.service.ts | 783 ++++++++++++++++++ packages/trpc/src/routers/chart.ts | 35 +- packages/trpc/src/routers/report.ts | 3 + packages/validation/src/index.ts | 67 +- 33 files changed, 2375 insertions(+), 148 deletions(-) create mode 100644 apps/justfuckinguseopenpanel/favicon.ico create mode 100644 apps/justfuckinguseopenpanel/index.html create mode 100644 apps/justfuckinguseopenpanel/ogimage.png create mode 100644 apps/justfuckinguseopenpanel/screenshots/dashboard-dark.webp create mode 100644 apps/justfuckinguseopenpanel/screenshots/overview-dark.webp create mode 100644 apps/justfuckinguseopenpanel/screenshots/profile-dark.webp create mode 100644 apps/justfuckinguseopenpanel/screenshots/realtime-dark.webp create mode 100644 apps/justfuckinguseopenpanel/screenshots/report-dark.webp create mode 100644 apps/justfuckinguseopenpanel/wrangler.jsonc create mode 100644 apps/start/src/components/report-chart/sankey/chart.tsx create mode 100644 apps/start/src/components/report-chart/sankey/index.tsx create mode 100644 apps/start/src/components/report/sidebar/ReportSeriesItem.tsx create mode 100644 apps/start/src/components/report/sidebar/report-fixed-events.tsx create mode 100644 packages/db/prisma/migrations/20251219125331_report_options/migration.sql create mode 100644 packages/db/src/services/sankey.service.ts diff --git a/apps/justfuckinguseopenpanel/favicon.ico b/apps/justfuckinguseopenpanel/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..d0979c25f9b57099917b5203247b3554e0fbdf3f GIT binary patch literal 15086 zcmeI3SEy7?5`gELbI$Q9DvIehV`L2viYO{ZFeiKwF)R8YMpQ(>2qGv3446^y#rPta zGp02liaBSk+4D7j&GzXz=iZsUtFyCspl*i>UER~6x~gv^k`c)r`RlKUmu5z`*N#M5 zMk4<%mxx6E>D4KIT%Jf|p+`^`8V`nh03To7f+lhQh*$9kQZxn`85xq!kSkX%QJ^21 zx6`nCnZkS||8$(uKCD1diq+_KG|EIqtg z$+Kl^-flzl>73@zpI;Q{hvqeP`$oOeDNbn>r}Z|S(rBGsw&}z0N^iGe*QH~=K!F01 z&QP#mK}i$}7cMN-t5=tnEn7<4wr!&myaJm%C~RdH78SrCPOWp**fGL1pv{gCa$WNSiioWb4+g=o%F>%a`}bELX0a?A*E2r#mu`yL|a_DP6j>k3LMFWa^m<%$Kk+59eZ9ZojaQ~>HbLG`i@SYK3z(dENNs{s#Gad|JAEk3-im>Atz<_#uqPM`1EIuZrr%h ztWR5ZW@e_`xN*Zr%WtrK`*uNZXtUFw^#UFHsV&>hyLie$FmT{NdH3!eYQ(;;U%y7j zjT;w8&;A)B{5#{#FR59xrlGG~xpJ1{&-w_`Bau4J6c?ELA3l8Wsr32t=jf0jL*i)R zWls5sy~4S3=cIP++J+we_wCykN5lGM{-D3BL!xE&2^U6<8WpO)OG`N`{lT-I!t`IW zW{sJXwp`|9pFVx0fB*hcw{G1y{cPHx{OHl6lhS|Qym@hB%lOx+Q^&N;`MrPt{x}-O z-`2lWsZ!<~KY#wbIqO(+w{G2%-o1MZxFB8GBU}j5-#hQ4Lx&EHqlGss{lRCaKkFMh z=G&erAwE_jqT~vr_K0-$HvKO{8=B+ zwQJW-I6Lfi$-jO3HrlUWzrePG^y%8QtGs&k$|vT>k00i&XRot)IGawMJQ>&K!Gi|{ zn+;lQ)-e6ou3amtf5(m;<>t+szIG`vmh2ta8Ft&`sdIs|>GI{vaXNYDt&A8kLTc5j zWyZ8oqee1+{(NJL+KsZ;E?&IYtV?8Pr$6f(I&4^PzT2Wq9{rgUuAY>!b6ns&WKO&1 zD)aK;!-rA!l1-a7$+2U{gx}WvJQ}m+R4)6;fddEP#)I|C__IEs%}#%nXQ$jh0Ikz9 z`xg6vs~csnUs2{0Y0$?x&6uk!=24jb>(;F^zh!p%4;wZtre~18+LoQNZ{4|bCvHCo z(wREOlXcs)Y15eQ$fGY|_VbDrE6n&q*P%lPxq9_#oOpg?&H`IE%`;cRfOR!|_;5LU z_N*}8S$&?pA-i|)me#FX$H`;OPnVZq^g$FFNp>(@%H*C>l-g*({Su&h$BR=E#yIOJwro$--XC`b|}Y8p@%k|f&C?&0oy}? zerVoKldXqWc{EjdZrQDO@ow3r*K)e1*iH)cLGy{G$?_yij~-d^a8~?*)QthQVLAi0 z%l{j2-$=Gk%A;v^{E5;9$+3Ce$3TC|zjp+9rj4Y~ym0PxOrqZ6r#Y3|*?<$mrXPRHg3wgx_6G z_1lJ2zflN^eN#t_YQlsG;>9G5PsH;-3H}LulP(Xmn>TMZv8hX!E*XE6OA}TOienR> ze*E}xpj+zY%a`V@g3UK*&>){n6hEkq&nmC+T21kbyng-K=g)E#@nTrUr>;EsYiybe z7cRu&z)BOA+HqONaoe_SK3Tkrz{kb7+I~^u_r&4g4{G0*Yv*ahEmH?zZ|XY=`t#_~ zBjZN`hfke0wV$3()5Jc&O_?$!Ru7&zqcK92tts`ICsjJ)a`>)PJ}GYqZ2XupV|@5t zy?PmZQ2QF^v-!2oPVLwe?`Q1xi2>O7!Gj0;@c3TAPn|l|S5NcTKpNUoS_Jr|sd1z%2Q~Nw!Wjob72r*;(ti-;@4(1%jrt#i{7@Jn%O*ad{MaSPpgpyfDPy}q@5R-llr4E? zy(&L_xp(iL=?i-+w8-SF!3N;n0(J=YCGv>B + + + + + + + Just Fucking Use OpenPanel - Stop Overpaying for Analytics + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+

Just Fucking Use OpenPanel

+

Stop settling for basic metrics. Get real insights that actually help you build a better product.

+
+ +
+
+
+
+
+
+
+
+ OpenPanel Real-time Analytics +
+
+
Real-time analytics - see events as they happen. No waiting, no delays.
+
+ +

The PostHog/Mixpanel Problem (Volume Pricing Hell)

+ +

Let's talk about what happens when you have a real product with real users.

+ +

Real pricing at scale (20M+ events/month):

+
    +
  • Mixpanel: $2,300/month (and more with add-ons)
  • +
  • PostHog: $1,982/month (and more with add-ons)
  • +
+ +

"1 million free events!" they scream. Cute. Until you have an actual product with actual users doing actual things. Then suddenly you need to "talk to sales" and your wallet starts bleeding.

+ +

Add-ons, add-ons everywhere. Session replay? +$X. Feature flags? +$X. HIPAA compliance? +$250/month. A/B testing? That'll be extra. You're hemorrhaging money just to understand what your users are doing, you magnificent fool.

+ +

The Web-Only Analytics Trap

+ +

You built a great fucking product. You have real traffic. Thousands, tens of thousands of visitors. But you're flying blind.

+ +
+ "Congrats, 50,000 visitors from France this month. Why didn't a single one buy your baguette?" +
+ +

You see the traffic. You see the bounce rate. You see the referrers. You see where they're from. You have NO FUCKING IDEA what users actually do.

+ +

Where do they drop off? Do they come back? What features do they use? Why didn't they convert? Who the fuck knows! You're using a glorified hit counter with a pretty dashboard that tells you everything about geography and nothing about behavior.

+ +

Plausible. Umami. Fathom. Simple Analytics. GoatCounter. Cabin. Pirsch. They're all the same story: simple analytics with some goals you can define. Page views, visitors, countries, basic funnels. That's it. No retention analysis. No user profiles. No event tracking. No cohorts. No revenue tracking. Just... basic web analytics.

+ +

And when you finally need to understand your users—when you need to see where they drop off in your signup flow, or which features drive retention, or why your conversion rate is shit—you end up paying for a SECOND tool on top. Now you're paying for two subscriptions, managing two dashboards, and your users' data is split across two platforms like a bad divorce.

+ +

Counter One Dollar Stats

+ +

"$1/month for page views. Adorable."

+ +

Look, I get it. A dollar is cheap. But you're getting exactly what you pay for: page views. That's it. No funnels. No retention. No user profiles. No event tracking. Just... page views.

+ +

Here's the thing: if you want to make good decisions about your product, you need to understand what your users are actually doing, not just where the fuck they're from.

+ +

OpenPanel gives you the full product analytics suite. Or self-host for FREE with UNLIMITED events.

+ +

You get:

+
    +
  • Funnels to see where users drop off
  • +
  • Retention analysis to see who comes back
  • +
  • Cohorts to segment your users
  • +
  • User profiles to understand individual behavior
  • +
  • Custom dashboards to see what matters to YOU
  • +
  • Revenue tracking to see what actually makes money
  • +
  • All the web analytics (page views, visitors, referrers) that the other tools give you
  • +
+ +

One Dollar Stats tells you 50,000 people visited from France. OpenPanel tells you why they didn't buy your baguette. That's the difference between vanity metrics and actual insights.

+ +

Why OpenPanel is the Answer

+ +

You want analytics that actually help you build a better product. Not vanity metrics. Not enterprise pricing. Not two separate tools.

+ +

To make good decisions, you need to understand what your users are doing, not just where they're from. You need to see where they drop off. You need to know which features they use. You need to understand why they convert or why they don't.

+ +
    +
  • Open Source & Self-Hostable: AGPL-3.0 - fork it, audit it, own it. Self-host for FREE with unlimited events, or use our cloud
  • +
  • Price: Affordable pricing that scales, or FREE self-hosted (unlimited events, forever)
  • +
  • SDK Size: 2.3KB (PostHog is 52KB+ - that's 22x bigger, you performance-obsessed maniac)
  • +
  • Privacy: Cookie-free by default, EU-only hosting (or your own servers if you self-host)
  • +
  • Full Suite: Web analytics + product analytics in one tool. No need for two subscriptions.
  • +
+ +
+
+
+
+
+
+
+
+ OpenPanel Overview Dashboard +
+
+
OpenPanel overview showing web analytics and product analytics in one clean interface
+
+ +

Open Source & Self-Hosting: The Ultimate Fuck You to Pricing Hell

+ +

Tired of watching your analytics bill grow every month? Tired of "talk to sales" when you hit their arbitrary limits? Tired of paying $2,000+/month just to understand your users?

+ +

OpenPanel is open source. AGPL-3.0 licensed. You can fork it. You can audit it. You can own it. And you can self-host it for FREE with UNLIMITED events.

+ +

That's right. Zero dollars. Unlimited events. All the features. Your data on your servers. No vendor lock-in. No surprise bills. No "enterprise sales" calls.

+ +

Mixpanel at 20M events? $2,300/month. PostHog? $1,982/month. OpenPanel self-hosted? $0/month. Forever.

+ +

Don't want to manage infrastructure? That's fine. Use our cloud. But if you want to escape the pricing hell entirely, self-hosting is a Docker command away. Your data, your rules, your wallet.

+ +

The Comparison Table (The Brutal Truth)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ToolPrice at 20M eventsWhat You Get
Mixpanel$2,300+/monthNot all feautres... since addons are extra
PostHog$1,982+/monthNot all feautres... since addons are extra
PlausibleVarious pricingSimple analytics with basic goals. Page views and visitors. That's it.
One Dollar Stats$1/monthPage views (but cheaper!)
OpenPanel~$530/mo or FREE (self-hosted)Web + Product analytics. The full package. Open source. Your data.
+ +
+
+
+
+
+
+
+
+ OpenPanel User Profiles +
+
+
User profiles - see individual user journeys and behavior. Something web-only tools can't give you.
+
+ +
+
+
+
+
+
+
+
+ OpenPanel Reports and Funnels +
+
+
Funnels, retention, and custom reports - the features you CAN'T get with web-only tools
+
+ +

The Bottom Fucking Line

+ +

If you want to make good decisions about your product, you need to understand what your users are actually doing. Not just where they're from. Not just how many page views you got. You need to see the full picture: funnels, retention, user behavior, conversion paths.

+ +

You have three choices:

+ +
    +
  1. Keep using Google Analytics like a data-harvesting accomplice, adding cookie banners, annoying your users, and contributing to the dystopian surveillance economy
  2. +
  3. Pay $2,000+/month for Mixpanel or PostHog when you scale, or use simple web-only analytics that tell you nothing about user behavior—just where they're from
  4. +
  5. Use OpenPanel (affordable pricing or FREE self-hosted) and get the full analytics suite: web analytics AND product analytics in one tool, so you can actually understand what your users do
  6. +
+ +

If you picked option 1 or 2, I can't help you. You're beyond saving. Go enjoy your complicated, privacy-violating, overpriced analytics life where you know everything about where your users are from but nothing about what they actually do.

+ +

But if you have even one functioning brain cell, you'll realize that OpenPanel gives you everything you need—web analytics AND product analytics—for a fraction of what the enterprise tools cost. You'll finally understand what your users are doing, not just where the fuck they're from.

+ +
+

Ready to understand what your users actually do?

+

Stop settling for vanity metrics. Get the full analytics suite—web analytics AND product analytics—so you can make better decisions. Or self-host for free.

+ Get Started with OpenPanel + Self-Host Guide +
+ +
+
+
+
+
+
+
+
+ OpenPanel Custom Dashboards +
+
+
Custom dashboards - build exactly what you need to understand your product
+
+ + +
+ + + + + diff --git a/apps/justfuckinguseopenpanel/ogimage.png b/apps/justfuckinguseopenpanel/ogimage.png new file mode 100644 index 0000000000000000000000000000000000000000..3c78a117ed1c5d1ea8680fd7d1b06de9fad932d5 GIT binary patch literal 63995 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 literal 0 HcmV?d00001 diff --git a/apps/justfuckinguseopenpanel/screenshots/dashboard-dark.webp b/apps/justfuckinguseopenpanel/screenshots/dashboard-dark.webp new file mode 100644 index 0000000000000000000000000000000000000000..ff4d74aa288456e1f82b5454b445c395ce30ee85 GIT binary patch literal 38112 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 literal 0 HcmV?d00001 diff --git a/apps/justfuckinguseopenpanel/screenshots/overview-dark.webp b/apps/justfuckinguseopenpanel/screenshots/overview-dark.webp new file mode 100644 index 0000000000000000000000000000000000000000..922b3b8d817604edda38774b09831da96411c53b GIT binary patch literal 47014 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¥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

ZIO z9_P5~S4CrLsVO6^Q-UpWgkeq|{Lm6iK_Z#t#FSLOvWYZj)CFhcG0}^wJejgDn(nHwef=2+9mMaACF1BF4!MhQ^z!ML!u(US2o21Q%0D)G(jBi~ZUoURZ zEeH&?E{4J9)mOB;OJ+-pj-?`#NID!K1?dm~DNjfM4F3l_EFuH0<7~6}XJmN&d79ND zxI%`w(00;t+6!23`FrTh5yZ&GOi~arHE)pwLCGI){Rlbvq2H8LBHwkSgTxRG@wX-?a3}Q-&G8i}yE)uNfN8XJ5&2 zHQ$i33;hvg%>&ma@R^C>PI5+Klrgvo6-f~i-$HT764ZGAJlG$iDcq=2NI0&&>xS~R}Y54+E<7&+|_gPzh+eYhinF!EKJJ5UpJ|b%& zL3DnM6(9#-wg%HHrosu=PCZ%I%@I6xTKN#+S;Ms&&RkLZcn4V@;W;TSp2r07{r@AK zw>pRy&eJ;S-rDhb;ckIQeiWmN(oTA!Rbu{b5Y_= zl!Z*`;erDGbO5XkDck@GiXjfT?~q<3Cog@u#X~^}kfEMuNKlKEmi2ndbbJG!=tgHP zPjly^Tld^~;<>_{@PITdxB|QgBk}EtEv+Sbx4BnB3JqQqaYld&hi6(Xtt#oK)@-&c z){fowZ$b-nr5M~a)?>R7Wf+t_Wz0P>)|8VFtn*L?$BWs*#PTeUX`R`aZ2VxtA=0FY zXfuQg=Ma$3L{Vm@;i<;Sg2SUU$&p$`o}v|O_I-l1Ex>=Jr~G1Vne67rkoN%tl~uGU z;RY_}gOjtVS{8lH#VVOI(K1*o11U2ATD!^i`t{jSZQoE$a(LYUliC(L#!vtN0GZ%| Am;e9( literal 0 HcmV?d00001 diff --git a/apps/justfuckinguseopenpanel/screenshots/realtime-dark.webp b/apps/justfuckinguseopenpanel/screenshots/realtime-dark.webp new file mode 100644 index 0000000000000000000000000000000000000000..5c1d5cc6e929319a7067de9baae9629023777e9c GIT binary patch literal 47506 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 literal 0 HcmV?d00001 diff --git a/apps/justfuckinguseopenpanel/screenshots/report-dark.webp b/apps/justfuckinguseopenpanel/screenshots/report-dark.webp new file mode 100644 index 0000000000000000000000000000000000000000..7cc2f15e0733bd57fa1141f3e082c5db2dad0391 GIT binary patch literal 48014 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 Mga)c1*#`jpFVV$G?f?J) literal 0 HcmV?d00001 diff --git a/apps/justfuckinguseopenpanel/wrangler.jsonc b/apps/justfuckinguseopenpanel/wrangler.jsonc new file mode 100644 index 00000000..005d95fb --- /dev/null +++ b/apps/justfuckinguseopenpanel/wrangler.jsonc @@ -0,0 +1,7 @@ +{ + "name": "justfuckinguseopenpanel", + "compatibility_date": "2025-12-19", + "assets": { + "directory": "." + } +} \ No newline at end of file diff --git a/apps/start/src/components/report-chart/index.tsx b/apps/start/src/components/report-chart/index.tsx index f116cf1f..95567b51 100644 --- a/apps/start/src/components/report-chart/index.tsx +++ b/apps/start/src/components/report-chart/index.tsx @@ -15,6 +15,7 @@ import { ReportMapChart } from './map'; import { ReportMetricChart } from './metric'; import { ReportPieChart } from './pie'; import { ReportRetentionChart } from './retention'; +import { ReportSankeyChart } from './sankey'; export const ReportChart = ({ lazy = true, ...props }: ReportChartProps) => { const ref = useRef(null); @@ -57,6 +58,8 @@ export const ReportChart = ({ lazy = true, ...props }: ReportChartProps) => { return ; case 'conversion': return ; + case 'sankey': + return ; default: return null; } diff --git a/apps/start/src/components/report-chart/report-editor.tsx b/apps/start/src/components/report-chart/report-editor.tsx index 28617064..d7ed60df 100644 --- a/apps/start/src/components/report-chart/report-editor.tsx +++ b/apps/start/src/components/report-chart/report-editor.tsx @@ -11,7 +11,6 @@ import { changeStartDate, ready, reset, - setName, setReport, } from '@/components/report/reportSlice'; import { ReportSidebar } from '@/components/report/sidebar/ReportSidebar'; @@ -20,7 +19,6 @@ import { Button } from '@/components/ui/button'; import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'; import { useAppParams } from '@/hooks/use-app-params'; import { useDispatch, useSelector } from '@/redux'; -import { bind } from 'bind-event-listener'; import { GanttChartSquareIcon } from 'lucide-react'; import { useEffect } from 'react'; diff --git a/apps/start/src/components/report-chart/sankey/chart.tsx b/apps/start/src/components/report-chart/sankey/chart.tsx new file mode 100644 index 00000000..57cd4a2d --- /dev/null +++ b/apps/start/src/components/report-chart/sankey/chart.tsx @@ -0,0 +1,302 @@ +import { + ChartTooltipContainer, + ChartTooltipHeader, + ChartTooltipItem, +} from '@/components/charts/chart-tooltip'; +import { useNumber } from '@/hooks/use-numer-formatter'; +import { round } from '@/utils/math'; +import { ResponsiveSankey } from '@nivo/sankey'; +import { + type ReactNode, + useLayoutEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { createPortal } from 'react-dom'; + +import { useTheme } from '@/components/theme-provider'; +import { truncate } from '@/utils/truncate'; +import { ArrowRightIcon } from 'lucide-react'; +import { AspectContainer } from '../aspect-container'; + +type PortalTooltipPosition = { left: number; top: number; ready: boolean }; + +function SankeyPortalTooltip({ + children, + offset = 12, + padding = 8, +}: { + children: ReactNode; + offset?: number; + padding?: number; +}) { + const anchorRef = useRef(null); + const tooltipRef = useRef(null); + const [anchorRect, setAnchorRect] = useState(null); + const [pos, setPos] = useState({ + left: 0, + top: 0, + ready: false, + }); + const [mounted, setMounted] = useState(false); + + useLayoutEffect(() => { + setMounted(true); + }, []); + + useLayoutEffect(() => { + const el = anchorRef.current; + if (!el) return; + + const wrapper = el.parentElement; + if (!wrapper) return; + + const update = () => { + setAnchorRect(wrapper.getBoundingClientRect()); + }; + + update(); + + const ro = new ResizeObserver(update); + ro.observe(wrapper); + + window.addEventListener('scroll', update, true); + window.addEventListener('resize', update); + + return () => { + ro.disconnect(); + window.removeEventListener('scroll', update, true); + window.removeEventListener('resize', update); + }; + }, []); + + useLayoutEffect(() => { + if (!mounted) return; + if (!anchorRect) return; + const tooltipEl = tooltipRef.current; + if (!tooltipEl) return; + + const rect = tooltipEl.getBoundingClientRect(); + const vw = window.innerWidth; + const vh = window.innerHeight; + + let left = anchorRect.left + offset; + let top = anchorRect.top + offset; + + left = Math.min( + Math.max(padding, left), + Math.max(padding, vw - rect.width - padding), + ); + top = Math.min( + Math.max(padding, top), + Math.max(padding, vh - rect.height - padding), + ); + + setPos({ left, top, ready: true }); + }, [mounted, anchorRect, children, offset, padding]); + + if (typeof document === 'undefined') { + return <>{children}; + } + + return ( + <> + + {mounted && + createPortal( +

, + document.body, + )} + + ); +} + +type SankeyData = { + nodes: Array<{ + id: string; + label: string; + nodeColor: string; + percentage?: number; + value?: number; + step?: number; + }>; + links: Array<{ source: string; target: string; value: number }>; +}; + +export function Chart({ data }: { data: SankeyData }) { + const number = useNumber(); + const containerRef = useRef(null); + const { appTheme } = useTheme(); + + // Process data for Sankey + const sankeyData = useMemo(() => { + if (!data) return { nodes: [], links: [] }; + + return { + nodes: data.nodes.map((node) => ({ + ...node, + label: node.label || node.id, + data: { + percentage: node.percentage, + value: node.value, + step: node.step, + label: node.label || node.id, + }, + })), + links: data.links, + }; + }, [data]); + + const totalSessions = useMemo(() => { + if (!sankeyData.nodes || sankeyData.nodes.length === 0) return 0; + const step1 = sankeyData.nodes.filter((n: any) => n.data?.step === 1); + const base = step1.length > 0 ? step1 : sankeyData.nodes; + return base.reduce((sum: number, n: any) => sum + (n.data?.value ?? 0), 0); + }, [sankeyData.nodes]); + + return ( + +
+ node.nodeColor} + nodeBorderRadius={2} + animate={false} + nodeBorderWidth={0} + nodeOpacity={0.8} + linkContract={1} + linkOpacity={0.3} + linkBlendMode={'normal'} + nodeTooltip={({ node }: any) => { + const label = node?.data?.label ?? node?.label ?? node?.id; + const value = node?.data?.value ?? node?.value ?? 0; + const step = node?.data?.step; + const pct = + typeof node?.data?.percentage === 'number' + ? node.data.percentage + : totalSessions > 0 + ? (value / totalSessions) * 100 + : 0; + const color = + node?.color ?? + node?.data?.nodeColor ?? + node?.data?.color ?? + node?.nodeColor ?? + '#64748b'; + + return ( + + + +
+ {label} +
+ {typeof step === 'number' && ( +
+ Step {step} +
+ )} +
+ +
+
Sessions
+
{number.format(value)}
+
+
+
Share
+
{number.format(round(pct, 1))} %
+
+
+
+
+ ); + }} + linkTooltip={({ link }: any) => { + const sourceLabel = + link?.source?.data?.label ?? + link?.source?.label ?? + link?.source?.id; + const targetLabel = + link?.target?.data?.label ?? + link?.target?.label ?? + link?.target?.id; + + const value = link?.value ?? 0; + const sourceValue = + link?.source?.data?.value ?? link?.source?.value ?? 0; + + const pctOfTotal = + totalSessions > 0 ? (value / totalSessions) * 100 : 0; + const pctOfSource = + sourceValue > 0 ? (value / sourceValue) * 100 : 0; + + const sourceStep = link?.source?.data?.step; + const targetStep = link?.target?.data?.step; + + const color = + link?.color ?? + link?.source?.color ?? + link?.source?.data?.nodeColor ?? + '#64748b'; + + return ( + + + +
+ {sourceLabel} + + {targetLabel} +
+ {typeof sourceStep === 'number' && + typeof targetStep === 'number' && ( +
+ {sourceStep} → {targetStep} +
+ )} +
+ + +
+
Sessions
+
{number.format(value)}
+
+
+
% of total
+
{number.format(round(pctOfTotal, 1))} %
+
+
+
% of source
+
{number.format(round(pctOfSource, 1))} %
+
+
+
+
+ ); + }} + label={(node: any) => { + const label = node.data?.label || node.label || node.id; + return truncate(label, 30, 'middle'); + }} + labelTextColor={appTheme === 'dark' ? '#e2e8f0' : '#0f172a'} + nodeSpacing={10} + /> +
+
+ ); +} diff --git a/apps/start/src/components/report-chart/sankey/index.tsx b/apps/start/src/components/report-chart/sankey/index.tsx new file mode 100644 index 00000000..9a576197 --- /dev/null +++ b/apps/start/src/components/report-chart/sankey/index.tsx @@ -0,0 +1,93 @@ +import { useTRPC } from '@/integrations/trpc/react'; +import { useQuery } from '@tanstack/react-query'; + +import type { IChartInput } from '@openpanel/validation'; + +import { AspectContainer } from '../aspect-container'; +import { ReportChartEmpty } from '../common/empty'; +import { ReportChartError } from '../common/error'; +import { ReportChartLoading } from '../common/loading'; +import { useReportChartContext } from '../context'; +import { Chart } from './chart'; + +export function ReportSankeyChart() { + const { + report: { + series, + range, + projectId, + options, + startDate, + endDate, + breakdowns, + }, + isLazyLoading, + } = useReportChartContext(); + + if (!options) { + return ; + } + + const input: IChartInput = { + series, + range, + projectId, + interval: 'day', + chartType: 'sankey', + breakdowns, + options, + metric: 'sum', + startDate, + endDate, + limit: 20, + previous: false, + }; + const trpc = useTRPC(); + const res = useQuery( + trpc.chart.sankey.queryOptions(input, { + enabled: !isLazyLoading && input.series.length > 0, + }), + ); + + if (isLazyLoading || res.isLoading) { + return ; + } + + if (res.isError) { + return ; + } + + if (!res.data || res.data.nodes.length === 0) { + return ; + } + + return ( +
+ +
+ ); +} + +function Loading() { + return ( + + + + ); +} + +function Error() { + return ( + + + + ); +} + +function Empty() { + return ( + + + + ); +} diff --git a/apps/start/src/components/report/ReportChartType.tsx b/apps/start/src/components/report/ReportChartType.tsx index ccc6f822..4d6129e6 100644 --- a/apps/start/src/components/report/ReportChartType.tsx +++ b/apps/start/src/components/report/ReportChartType.tsx @@ -5,6 +5,7 @@ import { ChartColumnIncreasingIcon, ConeIcon, GaugeIcon, + GitBranchIcon, Globe2Icon, LineChartIcon, type LucideIcon, @@ -58,6 +59,7 @@ export function ReportChartType({ retention: UsersIcon, map: Globe2Icon, conversion: TrendingUpIcon, + sankey: GitBranchIcon, }; return ( diff --git a/apps/start/src/components/report/reportSlice.ts b/apps/start/src/components/report/reportSlice.ts index 9ec30e32..80d07539 100644 --- a/apps/start/src/components/report/reportSlice.ts +++ b/apps/start/src/components/report/reportSlice.ts @@ -1,6 +1,5 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import { endOfDay, format, isSameDay, isSameMonth, startOfDay } from 'date-fns'; import { shortId } from '@openpanel/common'; import { @@ -12,12 +11,12 @@ import { import type { IChartBreakdown, IChartEventItem, - IChartFormula, IChartLineType, IChartProps, IChartRange, IChartType, IInterval, + IReportOptions, UnionOmit, zCriteria, } from '@openpanel/validation'; @@ -28,6 +27,7 @@ type InitialState = IChartProps & { ready: boolean; startDate: string | null; endDate: string | null; + options?: IReportOptions; }; // First approach: define the initial state using that type @@ -53,6 +53,7 @@ const initialState: InitialState = { criteria: 'on_or_after', funnelGroup: undefined, funnelWindow: undefined, + options: undefined, }; export const reportSlice = createSlice({ @@ -187,6 +188,16 @@ export const reportSlice = createSlice({ state.dirty = true; state.chartType = action.payload; + // Initialize sankey options if switching to sankey + if (action.payload === 'sankey' && !state.options) { + state.options = { + type: 'sankey', + mode: 'after', + steps: 5, + exclude: [], + }; + } + if ( !isMinuteIntervalEnabledByRange(state.range) && state.interval === 'minute' @@ -271,6 +282,66 @@ export const reportSlice = createSlice({ state.dirty = true; state.funnelWindow = action.payload || undefined; }, + changeOptions(state, action: PayloadAction) { + state.dirty = true; + state.options = action.payload || undefined; + }, + changeSankeyMode( + state, + action: PayloadAction<'between' | 'after' | 'before'>, + ) { + state.dirty = true; + if (!state.options) { + state.options = { + type: 'sankey', + mode: action.payload, + steps: 5, + exclude: [], + }; + } else if (state.options.type === 'sankey') { + state.options.mode = action.payload; + } + }, + changeSankeySteps(state, action: PayloadAction) { + state.dirty = true; + if (!state.options) { + state.options = { + type: 'sankey', + mode: 'after', + steps: action.payload, + exclude: [], + }; + } else if (state.options.type === 'sankey') { + state.options.steps = action.payload; + } + }, + changeSankeyExclude(state, action: PayloadAction) { + state.dirty = true; + if (!state.options) { + state.options = { + type: 'sankey', + mode: 'after', + steps: 5, + exclude: action.payload, + }; + } else if (state.options.type === 'sankey') { + state.options.exclude = action.payload; + } + }, + changeSankeyInclude(state, action: PayloadAction) { + state.dirty = true; + if (!state.options) { + state.options = { + type: 'sankey', + mode: 'after', + steps: 5, + exclude: [], + include: action.payload, + }; + } else if (state.options.type === 'sankey') { + state.options.include = action.payload; + } + }, reorderEvents( state, action: PayloadAction<{ fromIndex: number; toIndex: number }>, @@ -311,6 +382,11 @@ export const { changeUnit, changeFunnelGroup, changeFunnelWindow, + changeOptions, + changeSankeyMode, + changeSankeySteps, + changeSankeyExclude, + changeSankeyInclude, reorderEvents, } = reportSlice.actions; diff --git a/apps/start/src/components/report/sidebar/ReportSeries.tsx b/apps/start/src/components/report/sidebar/ReportSeries.tsx index 103cceec..02c6ae65 100644 --- a/apps/start/src/components/report/sidebar/ReportSeries.tsx +++ b/apps/start/src/components/report/sidebar/ReportSeries.tsx @@ -23,15 +23,13 @@ import { verticalListSortingStrategy, } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; -import { shortId } from '@openpanel/common'; import { alphabetIds } from '@openpanel/constants'; import type { IChartEvent, IChartEventItem, IChartFormula, } from '@openpanel/validation'; -import { FilterIcon, HandIcon, PiIcon } from 'lucide-react'; -import { ReportSegment } from '../ReportSegment'; +import { HandIcon, PiIcon, PlusIcon } from 'lucide-react'; import { addSerie, changeEvent, @@ -39,27 +37,21 @@ import { removeEvent, reorderEvents, } from '../reportSlice'; -import { EventPropertiesCombobox } from './EventPropertiesCombobox'; -import { PropertiesCombobox } from './PropertiesCombobox'; import type { ReportEventMoreProps } from './ReportEventMore'; import { ReportEventMore } from './ReportEventMore'; -import { FiltersList } from './filters/FiltersList'; +import { + ReportSeriesItem, + type ReportSeriesItemProps, +} from './ReportSeriesItem'; -function SortableSeries({ +function SortableReportSeriesItem({ event, index, showSegment, showAddFilter, isSelectManyEvents, ...props -}: { - event: IChartEventItem | IChartEvent; - index: number; - showSegment: boolean; - showAddFilter: boolean; - isSelectManyEvents: boolean; -} & React.HTMLAttributes) { - const dispatch = useDispatch(); +}: Omit) { const eventId = 'type' in event ? event.id : (event as IChartEvent).id; const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: eventId ?? '' }); @@ -69,85 +61,26 @@ function SortableSeries({ transition, }; - // Normalize event to have type field - const normalizedEvent: IChartEventItem = - 'type' in event ? event : { ...event, type: 'event' as const }; - - const isFormula = normalizedEvent.type === 'formula'; - const chartEvent = isFormula - ? null - : (normalizedEvent as IChartEventItem & { type: 'event' }); - return ( -
-
- - {props.children} -
- - {/* Segment and Filter buttons - only for events */} - {chartEvent && (showSegment || showAddFilter) && ( -
- {showSegment && ( - { - dispatch( - changeEvent({ - ...chartEvent, - segment, - }), - ); - }} - /> - )} - {showAddFilter && ( - { - dispatch( - changeEvent({ - ...chartEvent, - filters: [ - ...chartEvent.filters, - { - id: shortId(), - name: action.value, - operator: 'is', - value: [], - }, - ], - }), - ); - }} - > - {(setOpen) => ( - - )} - - )} - - {showSegment && chartEvent.segment.startsWith('property_') && ( - - )} -
- )} - - {/* Filters - only for events */} - {chartEvent && !isSelectManyEvents && } +
+ ( + + )} + {...props} + />
); } @@ -161,12 +94,23 @@ export function ReportSeries() { projectId, }); - const showSegment = !['retention', 'funnel'].includes(chartType); - const showAddFilter = !['retention'].includes(chartType); - const showDisplayNameInput = !['retention'].includes(chartType); + const showSegment = !['retention', 'funnel', 'sankey'].includes(chartType); + const showAddFilter = !['retention', 'sankey'].includes(chartType); + const showDisplayNameInput = !['retention', 'sankey'].includes(chartType); + const options = useSelector((state) => state.report.options); + const isSankey = chartType === 'sankey'; const isAddEventDisabled = (chartType === 'retention' || chartType === 'conversion') && selectedSeries.length >= 2; + const isSankeyEventLimitReached = + isSankey && + options && + ((options.type === 'sankey' && + options.mode === 'between' && + selectedSeries.length >= 2) || + (options.type === 'sankey' && + options.mode !== 'between' && + selectedSeries.length >= 1)); const dispatchChangeEvent = useDebounceFn((event: IChartEventItem) => { dispatch(changeEvent(event)); }); @@ -218,7 +162,8 @@ export function ReportSeries() { const showFormula = chartType !== 'conversion' && chartType !== 'funnel' && - chartType !== 'retention'; + chartType !== 'retention' && + chartType !== 'sankey'; return (
@@ -239,7 +184,7 @@ export function ReportSeries() { const isFormula = event.type === 'formula'; return ( - )} - + ); })}
{ @@ -393,6 +339,7 @@ export function ReportSeries() { type="button" variant="outline" icon={PiIcon} + className="flex-1 justify-start text-left" onClick={() => { dispatch( addSerie({ @@ -405,6 +352,7 @@ export function ReportSeries() { className="px-4" > Add Formula + )}
diff --git a/apps/start/src/components/report/sidebar/ReportSeriesItem.tsx b/apps/start/src/components/report/sidebar/ReportSeriesItem.tsx new file mode 100644 index 00000000..d647df98 --- /dev/null +++ b/apps/start/src/components/report/sidebar/ReportSeriesItem.tsx @@ -0,0 +1,114 @@ +import { ColorSquare } from '@/components/color-square'; +import { useDispatch } from '@/redux'; +import { shortId } from '@openpanel/common'; +import { alphabetIds } from '@openpanel/constants'; +import type { IChartEvent, IChartEventItem } from '@openpanel/validation'; +import { FilterIcon } from 'lucide-react'; +import { ReportSegment } from '../ReportSegment'; +import { changeEvent } from '../reportSlice'; +import { EventPropertiesCombobox } from './EventPropertiesCombobox'; +import { PropertiesCombobox } from './PropertiesCombobox'; +import { FiltersList } from './filters/FiltersList'; + +export interface ReportSeriesItemProps + extends React.HTMLAttributes { + event: IChartEventItem | IChartEvent; + index: number; + showSegment: boolean; + showAddFilter: boolean; + isSelectManyEvents: boolean; + renderDragHandle?: (index: number) => React.ReactNode; +} + +export function ReportSeriesItem({ + event, + index, + showSegment, + showAddFilter, + isSelectManyEvents, + renderDragHandle, + ...props +}: ReportSeriesItemProps) { + const dispatch = useDispatch(); + + // Normalize event to have type field + const normalizedEvent: IChartEventItem = + 'type' in event ? event : { ...event, type: 'event' as const }; + + const isFormula = normalizedEvent.type === 'formula'; + const chartEvent = isFormula + ? null + : (normalizedEvent as IChartEventItem & { type: 'event' }); + + return ( +
+
+ {renderDragHandle ? ( + renderDragHandle(index) + ) : ( + + {alphabetIds[index]} + + )} + {props.children} +
+ + {/* Segment and Filter buttons - only for events */} + {chartEvent && (showSegment || showAddFilter) && ( +
+ {showSegment && ( + { + dispatch( + changeEvent({ + ...chartEvent, + segment, + }), + ); + }} + /> + )} + {showAddFilter && ( + { + dispatch( + changeEvent({ + ...chartEvent, + filters: [ + ...chartEvent.filters, + { + id: shortId(), + name: action.value, + operator: 'is', + value: [], + }, + ], + }), + ); + }} + > + {(setOpen) => ( + + )} + + )} + + {showSegment && chartEvent.segment.startsWith('property_') && ( + + )} +
+ )} + + {/* Filters - only for events */} + {chartEvent && !isSelectManyEvents && } +
+ ); +} diff --git a/apps/start/src/components/report/sidebar/ReportSettings.tsx b/apps/start/src/components/report/sidebar/ReportSettings.tsx index a212db84..71107152 100644 --- a/apps/start/src/components/report/sidebar/ReportSettings.tsx +++ b/apps/start/src/components/report/sidebar/ReportSettings.tsx @@ -1,15 +1,22 @@ import { Combobox } from '@/components/ui/combobox'; import { useDispatch, useSelector } from '@/redux'; +import { ComboboxEvents } from '@/components/ui/combobox-events'; import { InputEnter } from '@/components/ui/input-enter'; import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; +import { useAppParams } from '@/hooks/use-app-params'; +import { useEventNames } from '@/hooks/use-event-names'; import { useMemo } from 'react'; import { changeCriteria, changeFunnelGroup, changeFunnelWindow, changePrevious, + changeSankeyExclude, + changeSankeyInclude, + changeSankeyMode, + changeSankeySteps, changeUnit, } from '../reportSlice'; @@ -20,13 +27,16 @@ export function ReportSettings() { const unit = useSelector((state) => state.report.unit); const funnelGroup = useSelector((state) => state.report.funnelGroup); const funnelWindow = useSelector((state) => state.report.funnelWindow); + const options = useSelector((state) => state.report.options); const dispatch = useDispatch(); + const { projectId } = useAppParams(); + const eventNames = useEventNames({ projectId }); const fields = useMemo(() => { const fields = []; - if (chartType !== 'retention') { + if (chartType !== 'retention' && chartType !== 'sankey') { fields.push('previous'); } @@ -40,6 +50,13 @@ export function ReportSettings() { fields.push('funnelWindow'); } + if (chartType === 'sankey') { + fields.push('sankeyMode'); + fields.push('sankeySteps'); + fields.push('sankeyExclude'); + fields.push('sankeyInclude'); + } + return fields; }, [chartType]); @@ -149,6 +166,89 @@ export function ReportSettings() { />
)} + {fields.includes('sankeyMode') && options?.type === 'sankey' && ( +
+ Mode + { + dispatch( + changeSankeyMode(val as 'between' | 'after' | 'before'), + ); + }} + items={[ + { + label: 'After', + value: 'after', + }, + { + label: 'Before', + value: 'before', + }, + { + label: 'Between', + value: 'between', + }, + ]} + /> +
+ )} + {fields.includes('sankeySteps') && options?.type === 'sankey' && ( +
+ Steps + { + const parsed = Number.parseInt(value, 10); + if (Number.isNaN(parsed) || parsed < 2 || parsed > 10) { + dispatch(changeSankeySteps(5)); + } else { + dispatch(changeSankeySteps(parsed)); + } + }} + /> +
+ )} + {fields.includes('sankeyExclude') && options?.type === 'sankey' && ( +
+ + Exclude Events + + { + dispatch(changeSankeyExclude(value)); + }} + items={eventNames.filter((item) => item.name !== '*')} + placeholder="Select events to exclude" + /> +
+ )} + {fields.includes('sankeyInclude') && options?.type === 'sankey' && ( +
+ + Include events + + { + dispatch( + changeSankeyInclude(value.length > 0 ? value : undefined), + ); + }} + items={eventNames.filter((item) => item.name !== '*')} + placeholder="Leave empty to include all" + /> +
+ )}
); diff --git a/apps/start/src/components/report/sidebar/ReportSidebar.tsx b/apps/start/src/components/report/sidebar/ReportSidebar.tsx index da28bb75..df8a48e8 100644 --- a/apps/start/src/components/report/sidebar/ReportSidebar.tsx +++ b/apps/start/src/components/report/sidebar/ReportSidebar.tsx @@ -5,14 +5,24 @@ import { useSelector } from '@/redux'; import { ReportBreakdowns } from './ReportBreakdowns'; import { ReportSeries } from './ReportSeries'; import { ReportSettings } from './ReportSettings'; +import { ReportFixedEvents } from './report-fixed-events'; export function ReportSidebar() { - const { chartType } = useSelector((state) => state.report); - const showBreakdown = chartType !== 'retention'; + const { chartType, options } = useSelector((state) => state.report); + const showBreakdown = chartType !== 'retention' && chartType !== 'sankey'; + const showFixedEvents = chartType === 'sankey'; return ( <>
- + {showFixedEvents ? ( + + ) : ( + + )} {showBreakdown && }
diff --git a/apps/start/src/components/report/sidebar/report-fixed-events.tsx b/apps/start/src/components/report/sidebar/report-fixed-events.tsx new file mode 100644 index 00000000..bdea470e --- /dev/null +++ b/apps/start/src/components/report/sidebar/report-fixed-events.tsx @@ -0,0 +1,223 @@ +import { ColorSquare } from '@/components/color-square'; +import { ComboboxEvents } from '@/components/ui/combobox-events'; +import { Input } from '@/components/ui/input'; +import { InputEnter } from '@/components/ui/input-enter'; +import { useAppParams } from '@/hooks/use-app-params'; +import { useDebounceFn } from '@/hooks/use-debounce-fn'; +import { useEventNames } from '@/hooks/use-event-names'; +import { useDispatch, useSelector } from '@/redux'; +import { alphabetIds } from '@openpanel/constants'; +import type { + IChartEvent, + IChartEventItem, + IChartFormula, +} from '@openpanel/validation'; +import { + addSerie, + changeEvent, + duplicateEvent, + removeEvent, +} from '../reportSlice'; +import type { ReportEventMoreProps } from './ReportEventMore'; +import { ReportEventMore } from './ReportEventMore'; +import { ReportSeriesItem } from './ReportSeriesItem'; + +export function ReportFixedEvents({ + numberOfEvents, +}: { + numberOfEvents: number; +}) { + const selectedSeries = useSelector((state) => state.report.series); + const chartType = useSelector((state) => state.report.chartType); + const dispatch = useDispatch(); + const { projectId } = useAppParams(); + const eventNames = useEventNames({ + projectId, + }); + + const showSegment = !['retention', 'funnel', 'sankey'].includes(chartType); + const showAddFilter = !['retention'].includes(chartType); + const showDisplayNameInput = !['retention', 'sankey'].includes(chartType); + const dispatchChangeEvent = useDebounceFn((event: IChartEventItem) => { + dispatch(changeEvent(event)); + }); + const isSelectManyEvents = chartType === 'retention'; + + const handleMore = (event: IChartEventItem | IChartEvent) => { + const callback: ReportEventMoreProps['onClick'] = (action) => { + switch (action) { + case 'remove': { + return dispatch( + removeEvent({ + id: 'type' in event ? event.id : (event as IChartEvent).id, + }), + ); + } + case 'duplicate': { + const normalized = + 'type' in event ? event : { ...event, type: 'event' as const }; + return dispatch(duplicateEvent(normalized)); + } + } + }; + + return callback; + }; + + const dispatchChangeFormula = useDebounceFn((formula: IChartFormula) => { + dispatch(changeEvent(formula)); + }); + + const showFormula = + chartType !== 'conversion' && + chartType !== 'funnel' && + chartType !== 'retention' && + chartType !== 'sankey'; + + return ( +
+

Metrics

+
+ {Array.from({ length: numberOfEvents }, (_, index) => ({ + slotId: `fixed-event-slot-${index}`, + index, + })).map(({ slotId, index }) => { + const event = selectedSeries[index]; + + // If no event exists at this index, render an empty slot + if (!event) { + return ( +
+
+ + {alphabetIds[index]} + + { + if (isSelectManyEvents) { + dispatch( + addSerie({ + type: 'event', + segment: 'user', + name: value, + filters: [ + { + name: 'name', + operator: 'is', + value: [value], + }, + ], + }), + ); + } else { + dispatch( + addSerie({ + type: 'event', + name: value, + segment: 'event', + filters: [], + }), + ); + } + }} + items={eventNames} + placeholder="Select event" + /> +
+
+ ); + } + + const isFormula = event.type === 'formula'; + if (isFormula) { + return null; + } + + return ( + + { + dispatch( + changeEvent( + Array.isArray(value) + ? { + id: event.id, + type: 'event', + segment: 'user', + filters: [ + { + name: 'name', + operator: 'is', + value: value, + }, + ], + name: '*', + } + : { + ...event, + type: 'event', + name: value, + filters: [], + }, + ), + ); + }} + items={eventNames} + placeholder="Select event" + /> + {showDisplayNameInput && ( + { + dispatchChangeEvent({ + ...(event as IChartEventItem & { + type: 'event'; + }), + displayName: e.target.value, + }); + }} + /> + )} + + + ); + })} +
+
+ ); +} diff --git a/apps/start/src/components/ui/combobox-events.tsx b/apps/start/src/components/ui/combobox-events.tsx index c9b3fd01..78e53c45 100644 --- a/apps/start/src/components/ui/combobox-events.tsx +++ b/apps/start/src/components/ui/combobox-events.tsx @@ -178,7 +178,7 @@ export function ComboboxEvents< Nothing selected { if (search === '') return true; return item.name.toLowerCase().includes(search.toLowerCase()); diff --git a/apps/start/src/routes/_app.$organizationId.$projectId.dashboards.tsx b/apps/start/src/routes/_app.$organizationId.$projectId.dashboards.tsx index 641ce539..7660d690 100644 --- a/apps/start/src/routes/_app.$organizationId.$projectId.dashboards.tsx +++ b/apps/start/src/routes/_app.$organizationId.$projectId.dashboards.tsx @@ -12,6 +12,7 @@ import { BarChartHorizontalIcon, ChartScatterIcon, ConeIcon, + GitBranchIcon, Globe2Icon, HashIcon, LayoutPanelTopIcon, @@ -153,6 +154,7 @@ function Component() { area: AreaChartIcon, retention: ChartScatterIcon, conversion: TrendingUpIcon, + sankey: GitBranchIcon, }[report.chartType]; return ( diff --git a/apps/start/src/routes/_app.$organizationId.$projectId.reports_.$reportId.tsx b/apps/start/src/routes/_app.$organizationId.$projectId.reports_.$reportId.tsx index 714ec89f..c60c92fb 100644 --- a/apps/start/src/routes/_app.$organizationId.$projectId.reports_.$reportId.tsx +++ b/apps/start/src/routes/_app.$organizationId.$projectId.reports_.$reportId.tsx @@ -36,5 +36,6 @@ function Component() { const { reportId } = Route.useParams(); const trpc = useTRPC(); const query = useSuspenseQuery(trpc.report.get.queryOptions({ reportId })); + console.log(query.data); return ; } diff --git a/packages/constants/index.ts b/packages/constants/index.ts index 013b2cfb..27cf4119 100644 --- a/packages/constants/index.ts +++ b/packages/constants/index.ts @@ -109,6 +109,7 @@ export const chartTypes = { funnel: 'Funnel', retention: 'Retention', conversion: 'Conversion', + sankey: 'Sankey', } as const; export const chartSegments = { diff --git a/packages/db/index.ts b/packages/db/index.ts index 51ca6b9f..06c41720 100644 --- a/packages/db/index.ts +++ b/packages/db/index.ts @@ -16,6 +16,7 @@ export * from './src/services/share.service'; export * from './src/services/session.service'; export * from './src/services/funnel.service'; export * from './src/services/conversion.service'; +export * from './src/services/sankey.service'; export * from './src/services/user.service'; export * from './src/services/reference.service'; export * from './src/services/id.service'; diff --git a/packages/db/prisma/migrations/20251219125331_report_options/migration.sql b/packages/db/prisma/migrations/20251219125331_report_options/migration.sql new file mode 100644 index 00000000..a393dd29 --- /dev/null +++ b/packages/db/prisma/migrations/20251219125331_report_options/migration.sql @@ -0,0 +1,5 @@ +-- AlterEnum +ALTER TYPE "public"."ChartType" ADD VALUE 'sankey'; + +-- AlterTable +ALTER TABLE "public"."reports" ADD COLUMN "options" JSONB; diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index 3b4d5dfe..b3985854 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -279,6 +279,7 @@ enum ChartType { funnel retention conversion + sankey } model Dashboard { @@ -321,6 +322,8 @@ model Report { criteria String? funnelGroup String? funnelWindow Float? + /// [IReportOptions] + options Json? dashboardId String dashboard Dashboard @relation(fields: [dashboardId], references: [id], onDelete: Cascade) diff --git a/packages/db/src/clickhouse/query-builder.ts b/packages/db/src/clickhouse/query-builder.ts index 80bf708c..cdbb23da 100644 --- a/packages/db/src/clickhouse/query-builder.ts +++ b/packages/db/src/clickhouse/query-builder.ts @@ -203,6 +203,13 @@ export class Query { return this; } + rawHaving(condition: string): this { + if (condition) { + this._having.push({ condition, operator: 'AND' }); + } + return this; + } + andHaving(column: string, operator: Operator, value: SqlParam): this { const condition = this.buildCondition(column, operator, value); this._having.push({ condition, operator: 'AND' }); diff --git a/packages/db/src/services/reports.service.ts b/packages/db/src/services/reports.service.ts index 7f8c79c4..7ed6fd3a 100644 --- a/packages/db/src/services/reports.service.ts +++ b/packages/db/src/services/reports.service.ts @@ -10,7 +10,7 @@ import type { IChartLineType, IChartProps, IChartRange, - ICriteria, + IReportOptions, } from '@openpanel/validation'; import type { Report as DbReport, ReportLayout } from '../prisma-client'; @@ -65,7 +65,13 @@ export function transformReportEventItem( export function transformReport( report: DbReport & { layout?: ReportLayout | null }, -): IChartProps & { id: string; layout?: ReportLayout | null } { +): IChartProps & { + id: string; + layout?: ReportLayout | null; +} { + // Parse options from JSON field, fallback to legacy fields for backward compatibility + const options = report.options as IReportOptions | null | undefined; + return { id: report.id, projectId: report.projectId, @@ -84,9 +90,13 @@ export function transformReport( formula: report.formula ?? undefined, metric: report.metric ?? 'sum', unit: report.unit ?? undefined, - criteria: (report.criteria as ICriteria) ?? undefined, + criteria: (report.criteria ?? 'on_or_after') as + | 'on_or_after' + | 'on' + | undefined, funnelGroup: report.funnelGroup ?? undefined, funnelWindow: report.funnelWindow ?? undefined, + options: options ?? undefined, layout: report.layout ?? undefined, }; } diff --git a/packages/db/src/services/sankey.service.ts b/packages/db/src/services/sankey.service.ts new file mode 100644 index 00000000..64417c44 --- /dev/null +++ b/packages/db/src/services/sankey.service.ts @@ -0,0 +1,783 @@ +import { chartColors } from '@openpanel/constants'; +import { type IChartEventFilter, zChartEvent } from '@openpanel/validation'; +import { z } from 'zod'; +import { TABLE_NAMES, ch } from '../clickhouse/client'; +import { clix } from '../clickhouse/query-builder'; +import { getEventFiltersWhereClause } from './chart.service'; + +export const zGetSankeyInput = z.object({ + projectId: z.string(), + startDate: z.string(), + endDate: z.string(), + steps: z.number().min(2).max(10).default(5), + mode: z.enum(['between', 'after', 'before']), + startEvent: zChartEvent, + endEvent: zChartEvent.optional(), + exclude: z.array(z.string()).default([]), + include: z.array(z.string()).optional(), +}); + +export type IGetSankeyInput = z.infer & { + timezone: string; +}; + +export class SankeyService { + constructor(private client: typeof ch) {} + + getRawWhereClause(type: 'events' | 'sessions', filters: IChartEventFilter[]) { + const where = getEventFiltersWhereClause( + filters.map((item) => { + if (type === 'sessions') { + if (item.name === 'path') { + return { ...item, name: 'entry_path' }; + } + if (item.name === 'origin') { + return { ...item, name: 'entry_origin' }; + } + if (item.name.startsWith('properties.__query.utm_')) { + return { + ...item, + name: item.name.replace('properties.__query.utm_', 'utm_'), + }; + } + return item; + } + return item; + }), + ); + + return Object.values(where).join(' AND '); + } + + private buildEventNameFilter( + include: string[] | undefined, + exclude: string[], + startEventName: string | undefined, + endEventName: string | undefined, + ) { + if (include && include.length > 0) { + const eventNames = [...include, startEventName, endEventName] + .filter((item) => item !== undefined) + .map((e) => `'${e!.replace(/'/g, "''")}'`) + .join(', '); + return `name IN (${eventNames})`; + } + if (exclude.length > 0) { + const excludedNames = exclude + .map((e) => `'${e.replace(/'/g, "''")}'`) + .join(', '); + return `name NOT IN (${excludedNames})`; + } + return null; + } + + private buildSessionEventCTE( + event: z.infer, + projectId: string, + startDate: string, + endDate: string, + timezone: string, + ): ReturnType { + return clix(this.client, timezone) + .select<{ session_id: string }>(['session_id']) + .from(TABLE_NAMES.events) + .where('project_id', '=', projectId) + .where('name', '=', event.name) + .where('created_at', 'BETWEEN', [ + clix.datetime(startDate, 'toDateTime'), + clix.datetime(endDate, 'toDateTime'), + ]) + .rawWhere(this.getRawWhereClause('events', event.filters)) + .groupBy(['session_id']); + } + + private getModeConfig( + mode: 'after' | 'before' | 'between', + startEvent: z.infer | undefined, + endEvent: z.infer | undefined, + hasStartEventCTE: boolean, + hasEndEventCTE: boolean, + steps: number, + ): { sessionFilter: string; eventsSliceExpr: string } { + const defaultSliceExpr = `arraySlice(events_deduped, 1, ${steps})`; + + if (mode === 'after' && startEvent) { + const escapedStartEvent = startEvent.name.replace(/'/g, "''"); + const sessionFilter = hasStartEventCTE + ? 'session_id IN (SELECT session_id FROM start_event_sessions)' + : `arrayExists(x -> x = '${escapedStartEvent}', events_deduped)`; + const eventsSliceExpr = `arraySlice(events_deduped, arrayFirstIndex(x -> x = '${escapedStartEvent}', events_deduped), ${steps})`; + return { sessionFilter, eventsSliceExpr }; + } + + if (mode === 'before' && startEvent) { + const escapedStartEvent = startEvent.name.replace(/'/g, "''"); + const sessionFilter = hasStartEventCTE + ? 'session_id IN (SELECT session_id FROM start_event_sessions)' + : `arrayExists(x -> x = '${escapedStartEvent}', events_deduped)`; + const eventsSliceExpr = `arraySlice( + events_deduped, + greatest(1, arrayFirstIndex(x -> x = '${escapedStartEvent}', events_deduped) - ${steps} + 1), + arrayFirstIndex(x -> x = '${escapedStartEvent}', events_deduped) - greatest(1, arrayFirstIndex(x -> x = '${escapedStartEvent}', events_deduped) - ${steps} + 1) + 1 + )`; + return { sessionFilter, eventsSliceExpr }; + } + + if (mode === 'between' && startEvent && endEvent) { + const escapedStartEvent = startEvent.name.replace(/'/g, "''"); + const escapedEndEvent = endEvent.name.replace(/'/g, "''"); + let sessionFilter = ''; + if (hasStartEventCTE && hasEndEventCTE) { + sessionFilter = + 'session_id IN (SELECT session_id FROM start_event_sessions) AND session_id IN (SELECT session_id FROM end_event_sessions)'; + } else if (hasStartEventCTE) { + sessionFilter = `session_id IN (SELECT session_id FROM start_event_sessions) AND arrayExists(x -> x = '${escapedEndEvent}', events_deduped)`; + } else if (hasEndEventCTE) { + sessionFilter = `arrayExists(x -> x = '${escapedStartEvent}', events_deduped) AND session_id IN (SELECT session_id FROM end_event_sessions)`; + } else { + sessionFilter = `arrayExists(x -> x = '${escapedStartEvent}', events_deduped) AND arrayExists(x -> x = '${escapedEndEvent}', events_deduped)`; + } + return { sessionFilter, eventsSliceExpr: defaultSliceExpr }; + } + + return { sessionFilter: '', eventsSliceExpr: defaultSliceExpr }; + } + + private async executeBetweenMode( + sessionPathsQuery: ReturnType, + startEvent: z.infer, + endEvent: z.infer, + steps: number, + COLORS: string[], + timezone: string, + ): Promise<{ + nodes: Array<{ + id: string; + label: string; + nodeColor: string; + percentage?: number; + value?: number; + step?: number; + }>; + links: Array<{ source: string; target: string; value: number }>; + }> { + // Find sessions where startEvent comes before endEvent + const betweenSessionsQuery = clix(this.client, timezone) + .with('session_paths', sessionPathsQuery) + .select<{ + session_id: string; + events: string[]; + start_index: number; + end_index: number; + }>([ + 'session_id', + 'events', + `arrayFirstIndex(x -> x = '${startEvent.name.replace(/'/g, "''")}', events) as start_index`, + `arrayFirstIndex(x -> x = '${endEvent.name.replace(/'/g, "''")}', events) as end_index`, + ]) + .from('session_paths') + .having('start_index', '>', 0) + .having('end_index', '>', 0) + .rawHaving('start_index < end_index'); + + // Get the slice between start and end + const betweenPathsQuery = clix(this.client, timezone) + .with('between_sessions', betweenSessionsQuery) + .select<{ + session_id: string; + events: string[]; + entry_event: string; + }>([ + 'session_id', + 'arraySlice(events, start_index, end_index - start_index + 1) as events', + 'events[start_index] as entry_event', + ]) + .from('between_sessions'); + + // Get top entry events + const topEntriesQuery = clix(this.client, timezone) + .with('session_paths', betweenPathsQuery) + .select<{ entry_event: string; count: number }>([ + 'entry_event', + 'count() as count', + ]) + .from('session_paths') + .groupBy(['entry_event']) + .orderBy('count', 'DESC') + .limit(3); + + const topEntries = await topEntriesQuery.execute(); + + if (topEntries.length === 0) { + return { nodes: [], links: [] }; + } + + const topEntryEvents = topEntries.map((e) => e.entry_event); + const totalSessions = topEntries.reduce((sum, e) => sum + e.count, 0); + + // Get transitions for between mode + const transitionsQuery = clix(this.client, timezone) + .with('between_sessions', betweenSessionsQuery) + .with( + 'session_paths', + clix(this.client, timezone) + .select([ + 'session_id', + 'arraySlice(events, start_index, end_index - start_index + 1) as events', + ]) + .from('between_sessions') + .having('events[1]', 'IN', topEntryEvents), + ) + .select<{ + source: string; + target: string; + step: number; + value: number; + }>([ + 'pair.1 as source', + 'pair.2 as target', + 'pair.3 as step', + 'count() as value', + ]) + .from( + clix.exp( + '(SELECT arrayJoin(arrayMap(i -> (events[i], events[i + 1], i), range(1, length(events)))) as pair FROM session_paths WHERE length(events) >= 2)', + ), + ) + .groupBy(['source', 'target', 'step']) + .orderBy('step', 'ASC') + .orderBy('value', 'DESC'); + + const transitions = await transitionsQuery.execute(); + + return this.buildSankeyFromTransitions( + transitions, + topEntries, + totalSessions, + steps, + COLORS, + ); + } + + private async executeSimpleMode( + sessionPathsQuery: ReturnType, + steps: number, + COLORS: string[], + timezone: string, + ): Promise<{ + nodes: Array<{ + id: string; + label: string; + nodeColor: string; + percentage?: number; + value?: number; + step?: number; + }>; + links: Array<{ source: string; target: string; value: number }>; + }> { + // Get top entry events + const topEntriesQuery = clix(this.client, timezone) + .with('session_paths', sessionPathsQuery) + .select<{ entry_event: string; count: number }>([ + 'entry_event', + 'count() as count', + ]) + .from('session_paths') + .groupBy(['entry_event']) + .orderBy('count', 'DESC') + .limit(3); + + const topEntries = await topEntriesQuery.execute(); + + if (topEntries.length === 0) { + return { nodes: [], links: [] }; + } + + const topEntryEvents = topEntries.map((e) => e.entry_event); + const totalSessions = topEntries.reduce((sum, e) => sum + e.count, 0); + + // Get transitions + const transitionsQuery = clix(this.client, timezone) + .with('session_paths_base', sessionPathsQuery) + .with( + 'session_paths', + clix(this.client, timezone) + .select(['session_id', 'events']) + .from('session_paths_base') + .having('events[1]', 'IN', topEntryEvents), + ) + .select<{ + source: string; + target: string; + step: number; + value: number; + }>([ + 'pair.1 as source', + 'pair.2 as target', + 'pair.3 as step', + 'count() as value', + ]) + .from( + clix.exp( + '(SELECT arrayJoin(arrayMap(i -> (events[i], events[i + 1], i), range(1, length(events)))) as pair FROM session_paths WHERE length(events) >= 2)', + ), + ) + .groupBy(['source', 'target', 'step']) + .orderBy('step', 'ASC') + .orderBy('value', 'DESC'); + + const transitions = await transitionsQuery.execute(); + + return this.buildSankeyFromTransitions( + transitions, + topEntries, + totalSessions, + steps, + COLORS, + ); + } + + async getSankey({ + projectId, + startDate, + endDate, + steps = 5, + mode, + startEvent, + endEvent, + exclude = [], + include, + timezone, + }: IGetSankeyInput): Promise<{ + nodes: Array<{ + id: string; + label: string; + nodeColor: string; + percentage?: number; + value?: number; + step?: number; + }>; + links: Array<{ source: string; target: string; value: number }>; + }> { + const COLORS = chartColors.map((color) => color.main); + + // 1. Build event name filter + const eventNameFilter = this.buildEventNameFilter( + include, + exclude, + startEvent?.name, + endEvent?.name, + ); + + // 2. Build ordered events query + // For screen_view events, use the path instead of the event name for more meaningful flow visualization + const orderedEventsQuery = clix(this.client, timezone) + .select<{ + session_id: string; + event_name: string; + created_at: string; + }>([ + 'session_id', + // "if(name = 'screen_view', path, name) as event_name", + 'name as event_name', + 'created_at', + ]) + .from(TABLE_NAMES.events) + .where('project_id', '=', projectId) + .where('created_at', 'BETWEEN', [ + clix.datetime(startDate, 'toDateTime'), + clix.datetime(endDate, 'toDateTime'), + ]) + .orderBy('session_id', 'ASC') + .orderBy('created_at', 'ASC'); + + if (eventNameFilter) { + orderedEventsQuery.rawWhere(eventNameFilter); + } + + // 3. Build session event CTEs + const startEventCTE = startEvent + ? this.buildSessionEventCTE( + startEvent, + projectId, + startDate, + endDate, + timezone, + ) + : null; + const endEventCTE = + mode === 'between' && endEvent + ? this.buildSessionEventCTE( + endEvent, + projectId, + startDate, + endDate, + timezone, + ) + : null; + + // 4. Build deduped events CTE + const eventsDedupedCTE = clix(this.client, timezone) + .with('ordered_events', orderedEventsQuery) + .select<{ + session_id: string; + events_deduped: string[]; + }>([ + 'session_id', + `arrayFilter( + (x, i) -> i = 1 OR x != events_raw[i - 1], + groupArray(event_name) as events_raw, + arrayEnumerate(events_raw) + ) as events_deduped`, + ]) + .from('ordered_events') + .groupBy(['session_id']); + + // 5. Get mode-specific config + const { sessionFilter, eventsSliceExpr } = this.getModeConfig( + mode, + startEvent, + endEvent, + startEventCTE !== null, + endEventCTE !== null, + steps, + ); + + // 6. Build truncate expression (for 'after' mode) + const truncateAtRepeatExpr = `if( + arrayFirstIndex(x -> x > 1, arrayEnumerateUniq(events_sliced)) = 0, + events_sliced, + arraySlice( + events_sliced, + 1, + arrayFirstIndex(x -> x > 1, arrayEnumerateUniq(events_sliced)) - 1 + ) + )`; + const eventsExpr = + mode === 'before' ? 'events_sliced' : truncateAtRepeatExpr; + + // 7. Build session paths query with conditional CTEs + const eventCTEs: Array<{ name: string; query: ReturnType }> = + []; + if (startEventCTE) { + eventCTEs.push({ name: 'start_event_sessions', query: startEventCTE }); + } + if (endEventCTE) { + eventCTEs.push({ name: 'end_event_sessions', query: endEventCTE }); + } + + const sessionPathsQuery = eventCTEs + .reduce( + (builder, cte) => builder.with(cte.name, cte.query), + clix(this.client, timezone), + ) + .with('events_deduped_cte', eventsDedupedCTE) + .with( + 'events_sliced_cte', + clix(this.client, timezone) + .select<{ + session_id: string; + events_sliced: string[]; + }>(['session_id', `${eventsSliceExpr} as events_sliced`]) + .from('events_deduped_cte') + .rawHaving(sessionFilter || '1 = 1'), + ) + .select<{ + session_id: string; + entry_event: string; + events: string[]; + }>(['session_id', `${eventsExpr} as events`, 'events[1] as entry_event']) + .from('events_sliced_cte') + .having('length(events)', '>=', 2); + + // 8. Execute mode-specific logic + if (mode === 'between' && startEvent && endEvent) { + return this.executeBetweenMode( + sessionPathsQuery, + startEvent, + endEvent, + steps, + COLORS, + timezone, + ); + } + + return this.executeSimpleMode(sessionPathsQuery, steps, COLORS, timezone); + } + + private buildSankeyFromTransitions( + transitions: Array<{ + source: string; + target: string; + step: number; + value: number; + }>, + topEntries: Array<{ entry_event: string; count: number }>, + totalSessions: number, + steps: number, + COLORS: string[], + ) { + if (transitions.length === 0) { + return { nodes: [], links: [] }; + } + + const TOP_DESTINATIONS_PER_NODE = 3; + + // Build the sankey progressively step by step + const nodes = new Map< + string, + { event: string; value: number; step: number; color: string } + >(); + const links: Array<{ source: string; target: string; value: number }> = []; + + // Helper to create unique node ID + const getNodeId = (event: string, step: number) => `${event}::step${step}`; + + // Group transitions by step + const transitionsByStep = new Map(); + for (const t of transitions) { + if (!transitionsByStep.has(t.step)) { + transitionsByStep.set(t.step, []); + } + transitionsByStep.get(t.step)!.push(t); + } + + // Initialize with entry events (step 1) + const activeNodes = new Map(); // event -> nodeId + topEntries.forEach((entry, idx) => { + const nodeId = getNodeId(entry.entry_event, 1); + nodes.set(nodeId, { + event: entry.entry_event, + value: entry.count, + step: 1, + color: COLORS[idx % COLORS.length]!, + }); + activeNodes.set(entry.entry_event, nodeId); + }); + + // Process each step: from active nodes, find top destinations + for (let step = 1; step < steps; step++) { + const stepTransitions = transitionsByStep.get(step) || []; + const nextActiveNodes = new Map(); + + // For each currently active node, find its top destinations + for (const [sourceEvent, sourceNodeId] of activeNodes) { + // Get transitions FROM this source event + const fromSource = stepTransitions + .filter((t) => t.source === sourceEvent) + .sort((a, b) => b.value - a.value) + .slice(0, TOP_DESTINATIONS_PER_NODE); + + for (const t of fromSource) { + // Skip self-loops + if (t.source === t.target) continue; + + const targetNodeId = getNodeId(t.target, step + 1); + + // Add link using unique node IDs + links.push({ + source: sourceNodeId, + target: targetNodeId, + value: t.value, + }); + + // Add/update target node + const existing = nodes.get(targetNodeId); + if (existing) { + existing.value += t.value; + } else { + // Inherit color from source or assign new + const sourceData = nodes.get(sourceNodeId); + nodes.set(targetNodeId, { + event: t.target, + value: t.value, + step: step + 1, + color: sourceData?.color || COLORS[nodes.size % COLORS.length]!, + }); + } + + nextActiveNodes.set(t.target, targetNodeId); + } + } + + // Update active nodes for next iteration + activeNodes.clear(); + for (const [event, nodeId] of nextActiveNodes) { + activeNodes.set(event, nodeId); + } + + // Stop if no more nodes to process + if (activeNodes.size === 0) break; + } + + // Filter links by threshold (0.25% of total sessions) + const MIN_LINK_PERCENT = 0.25; + const minLinkValue = Math.ceil((totalSessions * MIN_LINK_PERCENT) / 100); + const filteredLinks = links.filter((link) => link.value >= minLinkValue); + + // Find all nodes referenced by remaining links + const referencedNodeIds = new Set(); + filteredLinks.forEach((link) => { + referencedNodeIds.add(link.source); + referencedNodeIds.add(link.target); + }); + + // Recompute node values from filtered links + const nodeValuesFromLinks = new Map(); + filteredLinks.forEach((link) => { + const current = nodeValuesFromLinks.get(link.target) || 0; + nodeValuesFromLinks.set(link.target, current + link.value); + }); + + // For entry nodes (step 1), only keep them if they have outgoing links after filtering + nodes.forEach((nodeData, nodeId) => { + if (nodeData.step === 1) { + const hasOutgoing = filteredLinks.some((l) => l.source === nodeId); + if (!hasOutgoing) { + referencedNodeIds.delete(nodeId); + } + } + }); + + // Build final nodes array sorted by step then value + const finalNodes = Array.from(nodes.entries()) + .filter(([id]) => referencedNodeIds.has(id)) + .map(([id, data]) => { + const value = + data.step === 1 + ? data.value + : nodeValuesFromLinks.get(id) || data.value; + return { + id, + label: data.event, + nodeColor: data.color, + percentage: (value / totalSessions) * 100, + value, + step: data.step, + }; + }) + .sort((a, b) => { + if (a.step !== b.step) return a.step - b.step; + return b.value - a.value; + }); + + // Sanity check: Ensure all link endpoints exist in nodes + const nodeIds = new Set(finalNodes.map((n) => n.id)); + const validLinks = filteredLinks.filter( + (link) => nodeIds.has(link.source) && nodeIds.has(link.target), + ); + + // Combine final nodes with the same event name + // A final node is one that has no outgoing links + const nodesWithOutgoing = new Set(validLinks.map((l) => l.source)); + const finalNodeIds = new Set( + finalNodes.filter((n) => !nodesWithOutgoing.has(n.id)).map((n) => n.id), + ); + + // Group final nodes by event name + const finalNodesByEvent = new Map(); + finalNodes.forEach((node) => { + if (finalNodeIds.has(node.id)) { + if (!finalNodesByEvent.has(node.label)) { + finalNodesByEvent.set(node.label, []); + } + finalNodesByEvent.get(node.label)!.push(node); + } + }); + + // Create merged nodes and remap links + const nodeIdRemap = new Map(); // old nodeId -> new merged nodeId + const mergedNodes = new Map(); // merged nodeId -> node data + + finalNodesByEvent.forEach((nodesToMerge, eventName) => { + if (nodesToMerge.length > 1) { + // Merge multiple final nodes with same event name + const maxStep = Math.max(...nodesToMerge.map((n) => n.step || 0)); + const totalValue = nodesToMerge.reduce( + (sum, n) => sum + (n.value || 0), + 0, + ); + const mergedNodeId = `${eventName}::final`; + const firstNode = nodesToMerge[0]!; + + // Create merged node at the maximum step + mergedNodes.set(mergedNodeId, { + id: mergedNodeId, + label: eventName, + nodeColor: firstNode.nodeColor, + percentage: (totalValue / totalSessions) * 100, + value: totalValue, + step: maxStep, + }); + + // Map all old node IDs to the merged node ID + nodesToMerge.forEach((node) => { + nodeIdRemap.set(node.id, mergedNodeId); + }); + } + }); + + // Update links to point to merged nodes + const remappedLinks = validLinks.map((link) => { + const newSource = nodeIdRemap.get(link.source) || link.source; + const newTarget = nodeIdRemap.get(link.target) || link.target; + return { + source: newSource, + target: newTarget, + value: link.value, + }; + }); + + // Combine merged nodes with non-final nodes + const nonFinalNodes = finalNodes.filter((n) => !finalNodeIds.has(n.id)); + const finalNodesList = Array.from(mergedNodes.values()); + + // Remove old final nodes that were merged + const mergedOldNodeIds = new Set(nodeIdRemap.keys()); + const remainingNodes = nonFinalNodes.filter( + (n) => !mergedOldNodeIds.has(n.id), + ); + + // Combine all nodes and sort + const allNodes = [...remainingNodes, ...finalNodesList].sort((a, b) => { + if (a.step !== b.step) return a.step! - b.step!; + return b.value! - a.value!; + }); + + // Aggregate links that now point to the same merged target + const linkMap = new Map(); // "source->target" -> value + remappedLinks.forEach((link) => { + const key = `${link.source}->${link.target}`; + linkMap.set(key, (linkMap.get(key) || 0) + link.value); + }); + + const aggregatedLinks = Array.from(linkMap.entries()) + .map(([key, value]) => { + const parts = key.split('->'); + if (parts.length !== 2) return null; + return { source: parts[0]!, target: parts[1]!, value }; + }) + .filter( + (link): link is { source: string; target: string; value: number } => + link !== null, + ); + + // Final sanity check: Ensure all link endpoints exist in nodes + const finalNodeIdsSet = new Set(allNodes.map((n) => n.id)); + const finalValidLinks: Array<{ + source: string; + target: string; + value: number; + }> = aggregatedLinks.filter( + (link) => + finalNodeIdsSet.has(link.source) && finalNodeIdsSet.has(link.target), + ); + + return { + nodes: allNodes, + links: finalValidLinks, + }; + } +} + +export const sankeyService = new SankeyService(ch); diff --git a/packages/trpc/src/routers/chart.ts b/packages/trpc/src/routers/chart.ts index 66badb85..e3895d61 100644 --- a/packages/trpc/src/routers/chart.ts +++ b/packages/trpc/src/routers/chart.ts @@ -22,11 +22,10 @@ import { getSelectPropertyKey, getSettingsForProject, onlyReportEvents, + sankeyService, } from '@openpanel/db'; import { type IChartEvent, - zChartEvent, - zChartEventFilter, zChartInput, zChartSeries, zCriteria, @@ -379,6 +378,38 @@ export const chartRouter = createTRPCRouter({ }; }), + sankey: protectedProcedure.input(zChartInput).query(async ({ input }) => { + const { timezone } = await getSettingsForProject(input.projectId); + const currentPeriod = getChartStartEndDate(input, timezone); + + // Extract sankey options + const options = input.options; + + if (!options || options.type !== 'sankey') { + throw new Error('Sankey options are required'); + } + + // Extract start/end events from series based on mode + const eventSeries = onlyReportEvents(input.series); + + if (!eventSeries[0]) { + throw new Error('Start and end events are required'); + } + + return sankeyService.getSankey({ + projectId: input.projectId, + startDate: currentPeriod.startDate, + endDate: currentPeriod.endDate, + steps: options.steps, + mode: options.mode, + startEvent: eventSeries[0], + endEvent: eventSeries[1], + exclude: options.exclude || [], + include: options.include, + timezone, + }); + }), + chart: publicProcedure // .use(cacher) .input(zChartInput) diff --git a/packages/trpc/src/routers/report.ts b/packages/trpc/src/routers/report.ts index b17acb8c..a7e94a0e 100644 --- a/packages/trpc/src/routers/report.ts +++ b/packages/trpc/src/routers/report.ts @@ -59,6 +59,7 @@ export const reportRouter = createTRPCRouter({ metric: report.metric === 'count' ? 'sum' : report.metric, funnelGroup: report.funnelGroup, funnelWindow: report.funnelWindow, + options: report.options, }, }); }), @@ -104,6 +105,7 @@ export const reportRouter = createTRPCRouter({ metric: report.metric === 'count' ? 'sum' : report.metric, funnelGroup: report.funnelGroup, funnelWindow: report.funnelWindow, + options: report.options, }, }); }), @@ -175,6 +177,7 @@ export const reportRouter = createTRPCRouter({ metric: report.metric, funnelGroup: report.funnelGroup, funnelWindow: report.funnelWindow, + options: report.options, }, }); }), diff --git a/packages/validation/src/index.ts b/packages/validation/src/index.ts index 250bf426..81f0b9b0 100644 --- a/packages/validation/src/index.ts +++ b/packages/validation/src/index.ts @@ -88,36 +88,11 @@ export const zChartBreakdown = z.object({ // Support both old format (array of events without type) and new format (array of event/formula items) // Preprocess to normalize: if item has 'type' field, use discriminated union; otherwise, add type: 'event' -export const zChartSeries = z.preprocess((val) => { - if (!val) return val; - let processedVal = val; - - // If the input is an object with numeric keys, convert it to an array - if (typeof val === 'object' && val !== null && !Array.isArray(val)) { - const keys = Object.keys(val).sort( - (a, b) => Number.parseInt(a) - Number.parseInt(b), - ); - processedVal = keys.map((key) => (val as any)[key]); - } - - if (!Array.isArray(processedVal)) return processedVal; - - return processedVal.map((item: any) => { - // If item already has type field, return as-is - if (item && typeof item === 'object' && 'type' in item) { - return item; - } - // Otherwise, add type: 'event' for backward compatibility - if (item && typeof item === 'object' && 'name' in item) { - return { ...item, type: 'event' }; - } - return item; - }); -}, z +export const zChartSeries = z .array(zChartEventItem) .describe( 'Array of series (events or formulas) to be tracked and displayed in the chart', - )); + ); // Keep zChartEvents as an alias for backward compatibility during migration export const zChartEvents = zChartSeries; @@ -135,6 +110,35 @@ export const zRange = z.enum(objectToZodEnums(timeWindows)); export const zCriteria = z.enum(['on_or_after', 'on']); +// Report Options - Discriminated union based on chart type +export const zFunnelOptions = z.object({ + type: z.literal('funnel'), + funnelGroup: z.string().optional(), + funnelWindow: z.number().optional(), +}); + +export const zRetentionOptions = z.object({ + type: z.literal('retention'), + criteria: zCriteria.optional(), +}); + +export const zSankeyOptions = z.object({ + type: z.literal('sankey'), + mode: z.enum(['between', 'after', 'before']), + steps: z.number().min(2).max(10).default(5), + exclude: z.array(z.string()).default([]), + include: z.array(z.string()).optional(), +}); + +export const zReportOptions = z.discriminatedUnion('type', [ + zFunnelOptions, + zRetentionOptions, + zSankeyOptions, +]); + +export type IReportOptions = z.infer; +export type ISankeyOptions = z.infer; + export const zChartInputBase = z.object({ chartType: zChartType .default('linear') @@ -200,15 +204,10 @@ export const zChartInputBase = z.object({ .number() .optional() .describe('Time window in hours for funnel analysis'), + options: zReportOptions.optional(), }); -export const zChartInput = z.preprocess((val) => { - if (val && typeof val === 'object' && 'events' in val && !('series' in val)) { - // Migrate old 'events' field to 'series' - return { ...val, series: val.events }; - } - return val; -}, zChartInputBase); +export const zChartInput = zChartInputBase; export const zReportInput = zChartInputBase.extend({ name: z.string().describe('The user-defined name for the report'), From ba79ac570cc4f08bbf0622ba283578830268ab4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Fri, 9 Jan 2026 19:58:24 +0100 Subject: [PATCH 4/7] wip --- .../components/auth/share-enter-password.tsx | 25 +- .../components/report-chart/area/index.tsx | 31 +- .../src/components/report-chart/bar/index.tsx | 31 +- .../src/components/report-chart/context.tsx | 6 +- .../report-chart/conversion/index.tsx | 31 +- .../components/report-chart/funnel/index.tsx | 58 ++- .../report-chart/histogram/index.tsx | 31 +- .../components/report-chart/line/index.tsx | 31 +- .../src/components/report-chart/map/index.tsx | 31 +- .../components/report-chart/metric/index.tsx | 31 +- .../src/components/report-chart/pie/index.tsx | 31 +- .../components/report-chart/report-editor.tsx | 17 +- .../report-chart/retention/index.tsx | 56 ++- apps/start/src/modals/index.tsx | 4 + .../src/modals/share-dashboard-modal.tsx | 97 +++++ apps/start/src/modals/share-report-modal.tsx | 91 ++++ apps/start/src/routeTree.gen.ts | 42 ++ ...Id.$projectId.dashboards_.$dashboardId.tsx | 9 +- .../src/routes/share.dashboard.$shareId.tsx | 281 +++++++++++++ .../src/routes/share.report.$shareId.tsx | 142 +++++++ .../migration.sql | 53 +++ packages/db/prisma/schema.prisma | 62 ++- packages/db/src/services/share.service.ts | 97 +++++ packages/trpc/src/routers/auth.ts | 21 +- packages/trpc/src/routers/chart.ts | 393 ++++++++++++++++++ packages/trpc/src/routers/share.ts | 215 +++++++++- packages/validation/src/index.ts | 17 + 27 files changed, 1826 insertions(+), 108 deletions(-) create mode 100644 apps/start/src/modals/share-dashboard-modal.tsx create mode 100644 apps/start/src/modals/share-report-modal.tsx create mode 100644 apps/start/src/routes/share.dashboard.$shareId.tsx create mode 100644 apps/start/src/routes/share.report.$shareId.tsx create mode 100644 packages/db/prisma/migrations/20260109144217_add_share_dashboard_and_report/migration.sql diff --git a/apps/start/src/components/auth/share-enter-password.tsx b/apps/start/src/components/auth/share-enter-password.tsx index a04759bb..b25dae61 100644 --- a/apps/start/src/components/auth/share-enter-password.tsx +++ b/apps/start/src/components/auth/share-enter-password.tsx @@ -8,7 +8,13 @@ import { LogoSquare } from '../logo'; import { Button } from '../ui/button'; import { Input } from '../ui/input'; -export function ShareEnterPassword({ shareId }: { shareId: string }) { +export function ShareEnterPassword({ + shareId, + shareType = 'overview', +}: { + shareId: string; + shareType?: 'overview' | 'dashboard' | 'report'; +}) { const trpc = useTRPC(); const mutation = useMutation( trpc.auth.signInShare.mutationOptions({ @@ -25,6 +31,7 @@ export function ShareEnterPassword({ shareId }: { shareId: string }) { defaultValues: { password: '', shareId, + shareType, }, }); @@ -32,6 +39,7 @@ export function ShareEnterPassword({ shareId }: { shareId: string }) { mutation.mutate({ password: data.password, shareId, + shareType, }); }); @@ -40,9 +48,20 @@ export function ShareEnterPassword({ shareId }: { shareId: string }) {
-
Overview is locked
+
+ {shareType === 'dashboard' + ? 'Dashboard is locked' + : shareType === 'report' + ? 'Report is locked' + : 'Overview is locked'} +
- Please enter correct password to access this overview + Please enter correct password to access this{' '} + {shareType === 'dashboard' + ? 'dashboard' + : shareType === 'report' + ? 'report' + : 'overview'}
diff --git a/apps/start/src/components/report-chart/area/index.tsx b/apps/start/src/components/report-chart/area/index.tsx index c8d3f74a..138d7c74 100644 --- a/apps/start/src/components/report-chart/area/index.tsx +++ b/apps/start/src/components/report-chart/area/index.tsx @@ -1,6 +1,7 @@ import { useTRPC } from '@/integrations/trpc/react'; import { keepPreviousData, useQuery } from '@tanstack/react-query'; +import { useOverviewOptions } from '@/components/overview/useOverviewOptions'; import { AspectContainer } from '../aspect-container'; import { ReportChartEmpty } from '../common/empty'; import { ReportChartError } from '../common/error'; @@ -9,15 +10,33 @@ import { useReportChartContext } from '../context'; import { Chart } from './chart'; export function ReportAreaChart() { - const { isLazyLoading, report } = useReportChartContext(); + const { isLazyLoading, report, shareId, shareType } = useReportChartContext(); const trpc = useTRPC(); + const { range, startDate, endDate, interval } = useOverviewOptions(); const res = useQuery( - trpc.chart.chart.queryOptions(report, { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }), + shareId && shareType && 'id' in report && report.id + ? trpc.chart.chartByReport.queryOptions( + { + reportId: report.id, + shareId, + shareType, + range: range ?? undefined, + startDate: startDate ?? undefined, + endDate: endDate ?? undefined, + interval: interval ?? undefined, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }, + ) + : trpc.chart.chart.queryOptions(report, { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }), ); if ( diff --git a/apps/start/src/components/report-chart/bar/index.tsx b/apps/start/src/components/report-chart/bar/index.tsx index 877f104d..bdd4349b 100644 --- a/apps/start/src/components/report-chart/bar/index.tsx +++ b/apps/start/src/components/report-chart/bar/index.tsx @@ -1,6 +1,7 @@ import { useTRPC } from '@/integrations/trpc/react'; import { keepPreviousData, useQuery } from '@tanstack/react-query'; +import { useOverviewOptions } from '@/components/overview/useOverviewOptions'; import { cn } from '@/utils/cn'; import { AspectContainer } from '../aspect-container'; import { ReportChartEmpty } from '../common/empty'; @@ -9,15 +10,33 @@ import { useReportChartContext } from '../context'; import { Chart } from './chart'; export function ReportBarChart() { - const { isLazyLoading, report } = useReportChartContext(); + const { isLazyLoading, report, shareId, shareType } = useReportChartContext(); const trpc = useTRPC(); + const { range, startDate, endDate, interval } = useOverviewOptions(); const res = useQuery( - trpc.chart.aggregate.queryOptions(report, { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }), + shareId && shareType && 'id' in report && report.id + ? trpc.chart.aggregateByReport.queryOptions( + { + reportId: report.id, + shareId, + shareType, + range: range ?? undefined, + startDate: startDate ?? undefined, + endDate: endDate ?? undefined, + interval: interval ?? undefined, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }, + ) + : trpc.chart.aggregate.queryOptions(report, { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }), ); if ( diff --git a/apps/start/src/components/report-chart/context.tsx b/apps/start/src/components/report-chart/context.tsx index 8dd60331..7b6a3159 100644 --- a/apps/start/src/components/report-chart/context.tsx +++ b/apps/start/src/components/report-chart/context.tsx @@ -28,9 +28,11 @@ export type ReportChartContextType = { onClick: () => void; }[]; }>; - report: IChartProps; + report: IChartProps & { id?: string }; isLazyLoading: boolean; isEditMode: boolean; + shareId?: string; + shareType?: 'dashboard' | 'report'; }; type ReportChartContextProviderProps = ReportChartContextType & { @@ -40,6 +42,8 @@ type ReportChartContextProviderProps = ReportChartContextType & { export type ReportChartProps = Partial & { report: IChartInput; lazy?: boolean; + shareId?: string; + shareType?: 'dashboard' | 'report'; }; const context = createContext(null); diff --git a/apps/start/src/components/report-chart/conversion/index.tsx b/apps/start/src/components/report-chart/conversion/index.tsx index fc75527b..fdf94a2d 100644 --- a/apps/start/src/components/report-chart/conversion/index.tsx +++ b/apps/start/src/components/report-chart/conversion/index.tsx @@ -1,6 +1,7 @@ import { useTRPC } from '@/integrations/trpc/react'; import { keepPreviousData, useQuery } from '@tanstack/react-query'; +import { useOverviewOptions } from '@/components/overview/useOverviewOptions'; import { cn } from '@/utils/cn'; import { AspectContainer } from '../aspect-container'; import { ReportChartEmpty } from '../common/empty'; @@ -11,15 +12,33 @@ import { Chart } from './chart'; import { Summary } from './summary'; export function ReportConversionChart() { - const { isLazyLoading, report } = useReportChartContext(); + const { isLazyLoading, report, shareId, shareType } = useReportChartContext(); const trpc = useTRPC(); + const { range, startDate, endDate, interval } = useOverviewOptions(); console.log(report.limit); const res = useQuery( - trpc.chart.conversion.queryOptions(report, { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }), + shareId && shareType && 'id' in report && report.id + ? trpc.chart.conversionByReport.queryOptions( + { + reportId: report.id, + shareId, + shareType, + range: range ?? undefined, + startDate: startDate ?? undefined, + endDate: endDate ?? undefined, + interval: interval ?? undefined, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }, + ) + : trpc.chart.conversion.queryOptions(report, { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }), ); if ( diff --git a/apps/start/src/components/report-chart/funnel/index.tsx b/apps/start/src/components/report-chart/funnel/index.tsx index 5fdda9c2..e7633239 100644 --- a/apps/start/src/components/report-chart/funnel/index.tsx +++ b/apps/start/src/components/report-chart/funnel/index.tsx @@ -2,6 +2,7 @@ import { useTRPC } from '@/integrations/trpc/react'; import type { RouterOutputs } from '@/trpc/client'; import { useQuery } from '@tanstack/react-query'; +import { useOverviewOptions } from '@/components/overview/useOverviewOptions'; import type { IChartInput } from '@openpanel/validation'; import { AspectContainer } from '../aspect-container'; @@ -14,6 +15,7 @@ import { Chart, Summary, Tables } from './chart'; export function ReportFunnelChart() { const { report: { + id, series, range, projectId, @@ -25,28 +27,48 @@ export function ReportFunnelChart() { breakdowns, }, isLazyLoading, + shareId, + shareType, } = useReportChartContext(); + const { range: overviewRange, startDate: overviewStartDate, endDate: overviewEndDate, interval: overviewInterval } = useOverviewOptions(); - const input: IChartInput = { - series, - range, - projectId, - interval: 'day', - chartType: 'funnel', - breakdowns, - funnelWindow, - funnelGroup, - previous, - metric: 'sum', - startDate, - endDate, - limit: 20, - }; const trpc = useTRPC(); const res = useQuery( - trpc.chart.funnel.queryOptions(input, { - enabled: !isLazyLoading && input.series.length > 0, - }), + shareId && shareType && id + ? trpc.chart.funnelByReport.queryOptions( + { + reportId: id, + shareId, + shareType, + range: overviewRange ?? undefined, + startDate: overviewStartDate ?? undefined, + endDate: overviewEndDate ?? undefined, + interval: overviewInterval ?? undefined, + }, + { + enabled: !isLazyLoading && series.length > 0, + }, + ) + : (() => { + const input: IChartInput = { + series, + range, + projectId, + interval: 'day', + chartType: 'funnel', + breakdowns, + funnelWindow, + funnelGroup, + previous, + metric: 'sum', + startDate, + endDate, + limit: 20, + }; + return trpc.chart.funnel.queryOptions(input, { + enabled: !isLazyLoading && input.series.length > 0, + }); + })(), ); if (isLazyLoading || res.isLoading) { diff --git a/apps/start/src/components/report-chart/histogram/index.tsx b/apps/start/src/components/report-chart/histogram/index.tsx index 1f6d0146..d8092833 100644 --- a/apps/start/src/components/report-chart/histogram/index.tsx +++ b/apps/start/src/components/report-chart/histogram/index.tsx @@ -1,6 +1,7 @@ import { useTRPC } from '@/integrations/trpc/react'; import { keepPreviousData, useQuery } from '@tanstack/react-query'; +import { useOverviewOptions } from '@/components/overview/useOverviewOptions'; import { AspectContainer } from '../aspect-container'; import { ReportChartEmpty } from '../common/empty'; import { ReportChartError } from '../common/error'; @@ -9,15 +10,33 @@ import { useReportChartContext } from '../context'; import { Chart } from './chart'; export function ReportHistogramChart() { - const { isLazyLoading, report } = useReportChartContext(); + const { isLazyLoading, report, shareId, shareType } = useReportChartContext(); const trpc = useTRPC(); + const { range, startDate, endDate, interval } = useOverviewOptions(); const res = useQuery( - trpc.chart.chart.queryOptions(report, { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }), + shareId && shareType && 'id' in report && report.id + ? trpc.chart.chartByReport.queryOptions( + { + reportId: report.id, + shareId, + shareType, + range: range ?? undefined, + startDate: startDate ?? undefined, + endDate: endDate ?? undefined, + interval: interval ?? undefined, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }, + ) + : trpc.chart.chart.queryOptions(report, { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }), ); if ( diff --git a/apps/start/src/components/report-chart/line/index.tsx b/apps/start/src/components/report-chart/line/index.tsx index 5c11c5d7..111e8458 100644 --- a/apps/start/src/components/report-chart/line/index.tsx +++ b/apps/start/src/components/report-chart/line/index.tsx @@ -2,6 +2,7 @@ import { useTRPC } from '@/integrations/trpc/react'; import { keepPreviousData, useQuery } from '@tanstack/react-query'; import { cn } from '@/utils/cn'; +import { useOverviewOptions } from '@/components/overview/useOverviewOptions'; import { AspectContainer } from '../aspect-container'; import { ReportChartEmpty } from '../common/empty'; import { ReportChartError } from '../common/error'; @@ -10,15 +11,33 @@ import { useReportChartContext } from '../context'; import { Chart } from './chart'; export function ReportLineChart() { - const { isLazyLoading, report } = useReportChartContext(); + const { isLazyLoading, report, shareId, shareType } = useReportChartContext(); const trpc = useTRPC(); + const { range, startDate, endDate, interval } = useOverviewOptions(); const res = useQuery( - trpc.chart.chart.queryOptions(report, { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }), + shareId && shareType && 'id' in report && report.id + ? trpc.chart.chartByReport.queryOptions( + { + reportId: report.id, + shareId, + shareType, + range: range ?? undefined, + startDate: startDate ?? undefined, + endDate: endDate ?? undefined, + interval: interval ?? undefined, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }, + ) + : trpc.chart.chart.queryOptions(report, { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }), ); if ( diff --git a/apps/start/src/components/report-chart/map/index.tsx b/apps/start/src/components/report-chart/map/index.tsx index d6ca11c7..7989c07b 100644 --- a/apps/start/src/components/report-chart/map/index.tsx +++ b/apps/start/src/components/report-chart/map/index.tsx @@ -1,6 +1,7 @@ import { useTRPC } from '@/integrations/trpc/react'; import { keepPreviousData, useQuery } from '@tanstack/react-query'; +import { useOverviewOptions } from '@/components/overview/useOverviewOptions'; import { AspectContainer } from '../aspect-container'; import { ReportChartEmpty } from '../common/empty'; import { ReportChartError } from '../common/error'; @@ -9,15 +10,33 @@ import { useReportChartContext } from '../context'; import { Chart } from './chart'; export function ReportMapChart() { - const { isLazyLoading, report } = useReportChartContext(); + const { isLazyLoading, report, shareId, shareType } = useReportChartContext(); const trpc = useTRPC(); + const { range, startDate, endDate, interval } = useOverviewOptions(); const res = useQuery( - trpc.chart.chart.queryOptions(report, { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }), + shareId && shareType && 'id' in report && report.id + ? trpc.chart.chartByReport.queryOptions( + { + reportId: report.id, + shareId, + shareType, + range: range ?? undefined, + startDate: startDate ?? undefined, + endDate: endDate ?? undefined, + interval: interval ?? undefined, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }, + ) + : trpc.chart.chart.queryOptions(report, { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }), ); if ( diff --git a/apps/start/src/components/report-chart/metric/index.tsx b/apps/start/src/components/report-chart/metric/index.tsx index 7d8e5829..c0b9153f 100644 --- a/apps/start/src/components/report-chart/metric/index.tsx +++ b/apps/start/src/components/report-chart/metric/index.tsx @@ -1,6 +1,7 @@ import { useTRPC } from '@/integrations/trpc/react'; import { keepPreviousData, useQuery } from '@tanstack/react-query'; +import { useOverviewOptions } from '@/components/overview/useOverviewOptions'; import { AspectContainer } from '../aspect-container'; import { ReportChartEmpty } from '../common/empty'; import { ReportChartError } from '../common/error'; @@ -8,15 +9,33 @@ import { useReportChartContext } from '../context'; import { Chart } from './chart'; export function ReportMetricChart() { - const { isLazyLoading, report } = useReportChartContext(); + const { isLazyLoading, report, shareId, shareType } = useReportChartContext(); const trpc = useTRPC(); + const { range, startDate, endDate, interval } = useOverviewOptions(); const res = useQuery( - trpc.chart.chart.queryOptions(report, { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }), + shareId && shareType && 'id' in report && report.id + ? trpc.chart.chartByReport.queryOptions( + { + reportId: report.id, + shareId, + shareType, + range: range ?? undefined, + startDate: startDate ?? undefined, + endDate: endDate ?? undefined, + interval: interval ?? undefined, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }, + ) + : trpc.chart.chart.queryOptions(report, { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }), ); if ( diff --git a/apps/start/src/components/report-chart/pie/index.tsx b/apps/start/src/components/report-chart/pie/index.tsx index bf2589cc..dc58942b 100644 --- a/apps/start/src/components/report-chart/pie/index.tsx +++ b/apps/start/src/components/report-chart/pie/index.tsx @@ -1,6 +1,7 @@ import { useTRPC } from '@/integrations/trpc/react'; import { keepPreviousData, useQuery } from '@tanstack/react-query'; +import { useOverviewOptions } from '@/components/overview/useOverviewOptions'; import { AspectContainer } from '../aspect-container'; import { ReportChartEmpty } from '../common/empty'; import { ReportChartError } from '../common/error'; @@ -9,15 +10,33 @@ import { useReportChartContext } from '../context'; import { Chart } from './chart'; export function ReportPieChart() { - const { isLazyLoading, report } = useReportChartContext(); + const { isLazyLoading, report, shareId, shareType } = useReportChartContext(); const trpc = useTRPC(); + const { range, startDate, endDate, interval } = useOverviewOptions(); const res = useQuery( - trpc.chart.aggregate.queryOptions(report, { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }), + shareId && shareType && 'id' in report && report.id + ? trpc.chart.aggregateByReport.queryOptions( + { + reportId: report.id, + shareId, + shareType, + range: range ?? undefined, + startDate: startDate ?? undefined, + endDate: endDate ?? undefined, + interval: interval ?? undefined, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }, + ) + : trpc.chart.aggregate.queryOptions(report, { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }), ); if ( diff --git a/apps/start/src/components/report-chart/report-editor.tsx b/apps/start/src/components/report-chart/report-editor.tsx index d7ed60df..dc1a04a9 100644 --- a/apps/start/src/components/report-chart/report-editor.tsx +++ b/apps/start/src/components/report-chart/report-editor.tsx @@ -18,8 +18,10 @@ import { TimeWindowPicker } from '@/components/time-window-picker'; import { Button } from '@/components/ui/button'; import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'; import { useAppParams } from '@/hooks/use-app-params'; +import { pushModal } from '@/modals'; import { useDispatch, useSelector } from '@/redux'; -import { GanttChartSquareIcon } from 'lucide-react'; +import { bind } from 'bind-event-listener'; +import { GanttChartSquareIcon, ShareIcon } from 'lucide-react'; import { useEffect } from 'react'; import type { IServiceReport } from '@openpanel/db'; @@ -52,8 +54,19 @@ export default function ReportEditor({ return (
-
+
+ {initialReport?.id && ( + + )}
diff --git a/apps/start/src/components/report-chart/retention/index.tsx b/apps/start/src/components/report-chart/retention/index.tsx index 58bbffff..550b0326 100644 --- a/apps/start/src/components/report-chart/retention/index.tsx +++ b/apps/start/src/components/report-chart/retention/index.tsx @@ -1,6 +1,7 @@ import { useTRPC } from '@/integrations/trpc/react'; import { keepPreviousData, useQuery } from '@tanstack/react-query'; +import { useOverviewOptions } from '@/components/overview/useOverviewOptions'; import { AspectContainer } from '../aspect-container'; import { ReportChartEmpty } from '../common/empty'; import { ReportChartError } from '../common/error'; @@ -12,6 +13,7 @@ import CohortTable from './table'; export function ReportRetentionChart() { const { report: { + id, series, range, projectId, @@ -21,7 +23,10 @@ export function ReportRetentionChart() { interval, }, isLazyLoading, + shareId, + shareType, } = useReportChartContext(); + const { range: overviewRange, startDate: overviewStartDate, endDate: overviewEndDate, interval: overviewInterval } = useOverviewOptions(); const eventSeries = series.filter((item) => item.type === 'event'); const firstEvent = (eventSeries[0]?.filters?.[0]?.value ?? []).map(String); const secondEvent = (eventSeries[1]?.filters?.[0]?.value ?? []).map(String); @@ -29,23 +34,40 @@ export function ReportRetentionChart() { firstEvent.length > 0 && secondEvent.length > 0 && !isLazyLoading; const trpc = useTRPC(); const res = useQuery( - trpc.chart.cohort.queryOptions( - { - firstEvent, - secondEvent, - projectId, - range, - startDate, - endDate, - criteria, - interval, - }, - { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: isEnabled, - }, - ), + shareId && shareType && id + ? trpc.chart.cohortByReport.queryOptions( + { + reportId: id, + shareId, + shareType, + range: overviewRange ?? undefined, + startDate: overviewStartDate ?? undefined, + endDate: overviewEndDate ?? undefined, + interval: overviewInterval ?? undefined, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: isEnabled, + }, + ) + : trpc.chart.cohort.queryOptions( + { + firstEvent, + secondEvent, + projectId, + range, + startDate, + endDate, + criteria, + interval, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: isEnabled, + }, + ), ); if (!isEnabled) { diff --git a/apps/start/src/modals/index.tsx b/apps/start/src/modals/index.tsx index e723eaf3..bc835c98 100644 --- a/apps/start/src/modals/index.tsx +++ b/apps/start/src/modals/index.tsx @@ -30,7 +30,9 @@ import OverviewFilters from './overview-filters'; import RequestPasswordReset from './request-reset-password'; import SaveReport from './save-report'; import SelectBillingPlan from './select-billing-plan'; +import ShareDashboardModal from './share-dashboard-modal'; import ShareOverviewModal from './share-overview-modal'; +import ShareReportModal from './share-report-modal'; import ViewChartUsers from './view-chart-users'; const modals = { @@ -51,6 +53,8 @@ const modals = { EditReport: EditReport, EditReference: EditReference, ShareOverviewModal: ShareOverviewModal, + ShareDashboardModal: ShareDashboardModal, + ShareReportModal: ShareReportModal, AddReference: AddReference, ViewChartUsers: ViewChartUsers, Instructions: Instructions, diff --git a/apps/start/src/modals/share-dashboard-modal.tsx b/apps/start/src/modals/share-dashboard-modal.tsx new file mode 100644 index 00000000..8ac87677 --- /dev/null +++ b/apps/start/src/modals/share-dashboard-modal.tsx @@ -0,0 +1,97 @@ +import { ButtonContainer } from '@/components/button-container'; +import { Button } from '@/components/ui/button'; +import { useAppParams } from '@/hooks/use-app-params'; +import { handleError } from '@/integrations/trpc/react'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useNavigate } from '@tanstack/react-router'; +import { useForm } from 'react-hook-form'; +import { toast } from 'sonner'; +import type { z } from 'zod'; + +import { zShareDashboard } from '@openpanel/validation'; + +import { Input } from '@/components/ui/input'; +import { useTRPC } from '@/integrations/trpc/react'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { popModal } from '.'; +import { ModalContent, ModalHeader } from './Modal/Container'; + +const validator = zShareDashboard; + +type IForm = z.infer; + +export default function ShareDashboardModal({ + dashboardId, +}: { + dashboardId: string; +}) { + const { projectId, organizationId } = useAppParams(); + const navigate = useNavigate(); + + const { register, handleSubmit } = useForm({ + resolver: zodResolver(validator), + defaultValues: { + public: true, + password: '', + projectId, + organizationId, + dashboardId, + }, + }); + + const trpc = useTRPC(); + const queryClient = useQueryClient(); + const mutation = useMutation( + trpc.share.createDashboard.mutationOptions({ + onError: handleError, + onSuccess(res) { + queryClient.invalidateQueries(trpc.share.dashboard.pathFilter()); + toast('Success', { + description: `Your dashboard is now ${ + res.public ? 'public' : 'private' + }`, + action: { + label: 'View', + onClick: () => + navigate({ + to: '/share/dashboard/$shareId', + params: { + shareId: res.id, + }, + }), + }, + }); + popModal(); + }, + }), + ); + + return ( + + + { + mutation.mutate(values); + })} + > + + + + + + + + ); +} + diff --git a/apps/start/src/modals/share-report-modal.tsx b/apps/start/src/modals/share-report-modal.tsx new file mode 100644 index 00000000..c3c1f473 --- /dev/null +++ b/apps/start/src/modals/share-report-modal.tsx @@ -0,0 +1,91 @@ +import { ButtonContainer } from '@/components/button-container'; +import { Button } from '@/components/ui/button'; +import { useAppParams } from '@/hooks/use-app-params'; +import { handleError } from '@/integrations/trpc/react'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useNavigate } from '@tanstack/react-router'; +import { useForm } from 'react-hook-form'; +import { toast } from 'sonner'; +import type { z } from 'zod'; + +import { zShareReport } from '@openpanel/validation'; + +import { Input } from '@/components/ui/input'; +import { useTRPC } from '@/integrations/trpc/react'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { popModal } from '.'; +import { ModalContent, ModalHeader } from './Modal/Container'; + +const validator = zShareReport; + +type IForm = z.infer; + +export default function ShareReportModal({ reportId }: { reportId: string }) { + const { projectId, organizationId } = useAppParams(); + const navigate = useNavigate(); + + const { register, handleSubmit } = useForm({ + resolver: zodResolver(validator), + defaultValues: { + public: true, + password: '', + projectId, + organizationId, + reportId, + }, + }); + + const trpc = useTRPC(); + const queryClient = useQueryClient(); + const mutation = useMutation( + trpc.share.createReport.mutationOptions({ + onError: handleError, + onSuccess(res) { + queryClient.invalidateQueries(trpc.share.report.pathFilter()); + toast('Success', { + description: `Your report is now ${res.public ? 'public' : 'private'}`, + action: { + label: 'View', + onClick: () => + navigate({ + to: '/share/report/$shareId', + params: { + shareId: res.id, + }, + }), + }, + }); + popModal(); + }, + }), + ); + + return ( + + +
{ + mutation.mutate(values); + })} + > + + + + + +
+
+ ); +} + diff --git a/apps/start/src/routeTree.gen.ts b/apps/start/src/routeTree.gen.ts index a75fc4b0..b392a3a2 100644 --- a/apps/start/src/routeTree.gen.ts +++ b/apps/start/src/routeTree.gen.ts @@ -23,7 +23,9 @@ import { Route as LoginResetPasswordRouteImport } from './routes/_login.reset-pa import { Route as LoginLoginRouteImport } from './routes/_login.login' import { Route as AppOrganizationIdRouteImport } from './routes/_app.$organizationId' import { Route as AppOrganizationIdIndexRouteImport } from './routes/_app.$organizationId.index' +import { Route as ShareReportShareIdRouteImport } from './routes/share.report.$shareId' import { Route as ShareOverviewShareIdRouteImport } from './routes/share.overview.$shareId' +import { Route as ShareDashboardShareIdRouteImport } from './routes/share.dashboard.$shareId' import { Route as StepsOnboardingProjectRouteImport } from './routes/_steps.onboarding.project' import { Route as AppOrganizationIdSettingsRouteImport } from './routes/_app.$organizationId.settings' import { Route as AppOrganizationIdBillingRouteImport } from './routes/_app.$organizationId.billing' @@ -164,11 +166,21 @@ const AppOrganizationIdIndexRoute = AppOrganizationIdIndexRouteImport.update({ path: '/', getParentRoute: () => AppOrganizationIdRoute, } as any) +const ShareReportShareIdRoute = ShareReportShareIdRouteImport.update({ + id: '/share/report/$shareId', + path: '/share/report/$shareId', + getParentRoute: () => rootRouteImport, +} as any) const ShareOverviewShareIdRoute = ShareOverviewShareIdRouteImport.update({ id: '/share/overview/$shareId', path: '/share/overview/$shareId', getParentRoute: () => rootRouteImport, } as any) +const ShareDashboardShareIdRoute = ShareDashboardShareIdRouteImport.update({ + id: '/share/dashboard/$shareId', + path: '/share/dashboard/$shareId', + getParentRoute: () => rootRouteImport, +} as any) const StepsOnboardingProjectRoute = StepsOnboardingProjectRouteImport.update({ id: '/onboarding/project', path: '/onboarding/project', @@ -498,7 +510,9 @@ export interface FileRoutesByFullPath { '/$organizationId/billing': typeof AppOrganizationIdBillingRoute '/$organizationId/settings': typeof AppOrganizationIdSettingsRoute '/onboarding/project': typeof StepsOnboardingProjectRoute + '/share/dashboard/$shareId': typeof ShareDashboardShareIdRoute '/share/overview/$shareId': typeof ShareOverviewShareIdRoute + '/share/report/$shareId': typeof ShareReportShareIdRoute '/$organizationId/': typeof AppOrganizationIdIndexRoute '/$organizationId/$projectId/chat': typeof AppOrganizationIdProjectIdChatRoute '/$organizationId/$projectId/dashboards': typeof AppOrganizationIdProjectIdDashboardsRoute @@ -556,7 +570,9 @@ export interface FileRoutesByTo { '/$organizationId/billing': typeof AppOrganizationIdBillingRoute '/$organizationId/settings': typeof AppOrganizationIdSettingsRoute '/onboarding/project': typeof StepsOnboardingProjectRoute + '/share/dashboard/$shareId': typeof ShareDashboardShareIdRoute '/share/overview/$shareId': typeof ShareOverviewShareIdRoute + '/share/report/$shareId': typeof ShareReportShareIdRoute '/$organizationId': typeof AppOrganizationIdIndexRoute '/$organizationId/$projectId/chat': typeof AppOrganizationIdProjectIdChatRoute '/$organizationId/$projectId/dashboards': typeof AppOrganizationIdProjectIdDashboardsRoute @@ -614,7 +630,9 @@ export interface FileRoutesById { '/_app/$organizationId/billing': typeof AppOrganizationIdBillingRoute '/_app/$organizationId/settings': typeof AppOrganizationIdSettingsRoute '/_steps/onboarding/project': typeof StepsOnboardingProjectRoute + '/share/dashboard/$shareId': typeof ShareDashboardShareIdRoute '/share/overview/$shareId': typeof ShareOverviewShareIdRoute + '/share/report/$shareId': typeof ShareReportShareIdRoute '/_app/$organizationId/': typeof AppOrganizationIdIndexRoute '/_app/$organizationId/$projectId/chat': typeof AppOrganizationIdProjectIdChatRoute '/_app/$organizationId/$projectId/dashboards': typeof AppOrganizationIdProjectIdDashboardsRoute @@ -683,7 +701,9 @@ export interface FileRouteTypes { | '/$organizationId/billing' | '/$organizationId/settings' | '/onboarding/project' + | '/share/dashboard/$shareId' | '/share/overview/$shareId' + | '/share/report/$shareId' | '/$organizationId/' | '/$organizationId/$projectId/chat' | '/$organizationId/$projectId/dashboards' @@ -741,7 +761,9 @@ export interface FileRouteTypes { | '/$organizationId/billing' | '/$organizationId/settings' | '/onboarding/project' + | '/share/dashboard/$shareId' | '/share/overview/$shareId' + | '/share/report/$shareId' | '/$organizationId' | '/$organizationId/$projectId/chat' | '/$organizationId/$projectId/dashboards' @@ -798,7 +820,9 @@ export interface FileRouteTypes { | '/_app/$organizationId/billing' | '/_app/$organizationId/settings' | '/_steps/onboarding/project' + | '/share/dashboard/$shareId' | '/share/overview/$shareId' + | '/share/report/$shareId' | '/_app/$organizationId/' | '/_app/$organizationId/$projectId/chat' | '/_app/$organizationId/$projectId/dashboards' @@ -862,7 +886,9 @@ export interface RootRouteChildren { StepsRoute: typeof StepsRouteWithChildren ApiConfigRoute: typeof ApiConfigRoute ApiHealthcheckRoute: typeof ApiHealthcheckRoute + ShareDashboardShareIdRoute: typeof ShareDashboardShareIdRoute ShareOverviewShareIdRoute: typeof ShareOverviewShareIdRoute + ShareReportShareIdRoute: typeof ShareReportShareIdRoute } declare module '@tanstack/react-router' { @@ -965,6 +991,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AppOrganizationIdIndexRouteImport parentRoute: typeof AppOrganizationIdRoute } + '/share/report/$shareId': { + id: '/share/report/$shareId' + path: '/share/report/$shareId' + fullPath: '/share/report/$shareId' + preLoaderRoute: typeof ShareReportShareIdRouteImport + parentRoute: typeof rootRouteImport + } '/share/overview/$shareId': { id: '/share/overview/$shareId' path: '/share/overview/$shareId' @@ -972,6 +1005,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ShareOverviewShareIdRouteImport parentRoute: typeof rootRouteImport } + '/share/dashboard/$shareId': { + id: '/share/dashboard/$shareId' + path: '/share/dashboard/$shareId' + fullPath: '/share/dashboard/$shareId' + preLoaderRoute: typeof ShareDashboardShareIdRouteImport + parentRoute: typeof rootRouteImport + } '/_steps/onboarding/project': { id: '/_steps/onboarding/project' path: '/onboarding/project' @@ -1751,7 +1791,9 @@ const rootRouteChildren: RootRouteChildren = { StepsRoute: StepsRouteWithChildren, ApiConfigRoute: ApiConfigRoute, ApiHealthcheckRoute: ApiHealthcheckRoute, + ShareDashboardShareIdRoute: ShareDashboardShareIdRoute, ShareOverviewShareIdRoute: ShareOverviewShareIdRoute, + ShareReportShareIdRoute: ShareReportShareIdRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) diff --git a/apps/start/src/routes/_app.$organizationId.$projectId.dashboards_.$dashboardId.tsx b/apps/start/src/routes/_app.$organizationId.$projectId.dashboards_.$dashboardId.tsx index 503cb1e8..5633bc34 100644 --- a/apps/start/src/routes/_app.$organizationId.$projectId.dashboards_.$dashboardId.tsx +++ b/apps/start/src/routes/_app.$organizationId.$projectId.dashboards_.$dashboardId.tsx @@ -17,6 +17,7 @@ import { MoreHorizontal, PlusIcon, RotateCcw, + ShareIcon, Trash, TrashIcon, } from 'lucide-react'; @@ -30,7 +31,7 @@ import { OverviewRange } from '@/components/overview/overview-range'; import { PageContainer } from '@/components/page-container'; import { PageHeader } from '@/components/page-header'; import { handleErrorToastOptions, useTRPC } from '@/integrations/trpc/react'; -import { showConfirm } from '@/modals'; +import { pushModal, showConfirm } from '@/modals'; import { useMutation, useQuery } from '@tanstack/react-query'; import { createFileRoute, useRouter } from '@tanstack/react-router'; import { useCallback, useEffect, useMemo, useState } from 'react'; @@ -484,6 +485,12 @@ function Component() { + pushModal('ShareDashboardModal', { dashboardId })} + > + + Share dashboard + showConfirm({ diff --git a/apps/start/src/routes/share.dashboard.$shareId.tsx b/apps/start/src/routes/share.dashboard.$shareId.tsx new file mode 100644 index 00000000..485bf438 --- /dev/null +++ b/apps/start/src/routes/share.dashboard.$shareId.tsx @@ -0,0 +1,281 @@ +import { ShareEnterPassword } from '@/components/auth/share-enter-password'; +import { FullPageEmptyState } from '@/components/full-page-empty-state'; +import FullPageLoadingState from '@/components/full-page-loading-state'; +import { LoginNavbar } from '@/components/login-navbar'; +import { ReportChart } from '@/components/report-chart'; +import { OverviewRange } from '@/components/overview/overview-range'; +import { OverviewInterval } from '@/components/overview/overview-interval'; +import { useOverviewOptions } from '@/components/overview/useOverviewOptions'; +import { PageContainer } from '@/components/page-container'; +import { useTRPC } from '@/integrations/trpc/react'; +import { useQuery, useSuspenseQuery } from '@tanstack/react-query'; +import { createFileRoute, notFound, useSearch } from '@tanstack/react-router'; +import { z } from 'zod'; +import { useMemo } from 'react'; +import { Responsive, WidthProvider } from 'react-grid-layout'; +import 'react-grid-layout/css/styles.css'; +import 'react-resizable/css/styles.css'; +import { cn } from '@/utils/cn'; +import { timeWindows } from '@openpanel/constants'; + +const ResponsiveGridLayout = WidthProvider(Responsive); + +type Layout = { + i: string; + x: number; + y: number; + w: number; + h: number; + minW?: number; + minH?: number; + maxW?: number; + maxH?: number; +}; + +const shareSearchSchema = z.object({ + header: z.optional(z.number().or(z.string().or(z.boolean()))), +}); + +export const Route = createFileRoute('/share/dashboard/$shareId')({ + component: RouteComponent, + validateSearch: shareSearchSchema, + loader: async ({ context, params }) => { + const share = await context.queryClient.ensureQueryData( + context.trpc.share.dashboard.queryOptions({ + shareId: params.shareId, + }), + ); + + return { share }; + }, + head: ({ loaderData }) => { + if (!loaderData || !loaderData.share) { + return { + meta: [ + { + title: 'Share not found - OpenPanel.dev', + }, + ], + }; + } + + return { + meta: [ + { + title: `${loaderData.share.dashboard?.name} - ${loaderData.share.organization?.name} - OpenPanel.dev`, + }, + ], + }; + }, + pendingComponent: FullPageLoadingState, + errorComponent: () => ( + + ), +}); + +// Report Item Component for shared view +function ReportItem({ + report, + shareId, + range, + startDate, + endDate, + interval, +}: { + report: any; + shareId: string; + range: any; + startDate: any; + endDate: any; + interval: any; +}) { + const chartRange = report.range; + + return ( +
+
+
+
{report.name}
+ {chartRange !== null && ( +
+ + {timeWindows[chartRange as keyof typeof timeWindows]?.label} + + {startDate && endDate ? ( + Custom dates + ) : ( + range !== null && + chartRange !== range && ( + + {timeWindows[range as keyof typeof timeWindows]?.label} + + ) + )} +
+ )} +
+
+
+ +
+
+ ); +} + +function RouteComponent() { + const { shareId } = Route.useParams(); + const { header } = useSearch({ from: '/share/dashboard/$shareId' }); + const trpc = useTRPC(); + const { range, startDate, endDate, interval } = useOverviewOptions(); + + const shareQuery = useSuspenseQuery( + trpc.share.dashboard.queryOptions({ + shareId, + }), + ); + + const reportsQuery = useQuery( + trpc.share.dashboardReports.queryOptions({ + shareId, + }), + ); + + const hasAccess = shareQuery.data?.hasAccess; + + if (!shareQuery.data) { + throw notFound(); + } + + if (!shareQuery.data.public) { + throw notFound(); + } + + const share = shareQuery.data; + + // Handle password protection + if (share.password && !hasAccess) { + return ( + + ); + } + + const isHeaderVisible = + header !== '0' && header !== 0 && header !== 'false' && header !== false; + + const reports = reportsQuery.data ?? []; + + // Convert reports to grid layout format for all breakpoints + const layouts = useMemo(() => { + const baseLayout = reports.map((report, index) => ({ + i: report.id, + x: report.layout?.x ?? (index % 2) * 6, + y: report.layout?.y ?? Math.floor(index / 2) * 4, + w: report.layout?.w ?? 6, + h: report.layout?.h ?? 4, + minW: 3, + minH: 3, + })); + + // Create responsive layouts for different breakpoints + return { + lg: baseLayout, + md: baseLayout, + sm: baseLayout.map((item) => ({ ...item, w: Math.min(item.w, 6) })), + xs: baseLayout.map((item) => ({ ...item, w: 4, x: 0 })), + xxs: baseLayout.map((item) => ({ ...item, w: 2, x: 0 })), + }; + }, [reports]); + + return ( +
+ {isHeaderVisible && ( +
+ +
+ )} + +
+
+
+
+ + +
+
+
+
+ {reports.length === 0 ? ( + + ) : ( +
+ + + {reports.map((report) => ( +
+ +
+ ))} +
+
+ )} +
+
+ ); +} + diff --git a/apps/start/src/routes/share.report.$shareId.tsx b/apps/start/src/routes/share.report.$shareId.tsx new file mode 100644 index 00000000..b9c7eb92 --- /dev/null +++ b/apps/start/src/routes/share.report.$shareId.tsx @@ -0,0 +1,142 @@ +import { ShareEnterPassword } from '@/components/auth/share-enter-password'; +import { FullPageEmptyState } from '@/components/full-page-empty-state'; +import FullPageLoadingState from '@/components/full-page-loading-state'; +import { LoginNavbar } from '@/components/login-navbar'; +import { ReportChart } from '@/components/report-chart'; +import { OverviewRange } from '@/components/overview/overview-range'; +import { OverviewInterval } from '@/components/overview/overview-interval'; +import { PageContainer } from '@/components/page-container'; +import { useTRPC } from '@/integrations/trpc/react'; +import { useSuspenseQuery } from '@tanstack/react-query'; +import { createFileRoute, notFound, useSearch } from '@tanstack/react-router'; +import { z } from 'zod'; + +const shareSearchSchema = z.object({ + header: z.optional(z.number().or(z.string().or(z.boolean()))), +}); + +export const Route = createFileRoute('/share/report/$shareId')({ + component: RouteComponent, + validateSearch: shareSearchSchema, + loader: async ({ context, params }) => { + const share = await context.queryClient.ensureQueryData( + context.trpc.share.report.queryOptions({ + shareId: params.shareId, + }), + ); + + if (!share) { + return { share: null }; + } + + const report = await context.queryClient.ensureQueryData( + context.trpc.report.get.queryOptions({ + reportId: share.reportId, + }), + ); + + return { share, report }; + }, + head: ({ loaderData }) => { + if (!loaderData || !loaderData.share) { + return { + meta: [ + { + title: 'Share not found - OpenPanel.dev', + }, + ], + }; + } + + return { + meta: [ + { + title: `${loaderData.report?.name || 'Report'} - ${loaderData.share.organization?.name} - OpenPanel.dev`, + }, + ], + }; + }, + pendingComponent: FullPageLoadingState, + errorComponent: () => ( + + ), +}); + +function RouteComponent() { + const { shareId } = Route.useParams(); + const { header } = useSearch({ from: '/share/report/$shareId' }); + const trpc = useTRPC(); + const shareQuery = useSuspenseQuery( + trpc.share.report.queryOptions({ + shareId, + }), + ); + + const reportQuery = useSuspenseQuery( + trpc.report.get.queryOptions({ + reportId: shareQuery.data!.reportId, + }), + ); + + const hasAccess = shareQuery.data?.hasAccess; + + if (!shareQuery.data) { + throw notFound(); + } + + if (!shareQuery.data.public) { + throw notFound(); + } + + const share = shareQuery.data; + const report = reportQuery.data; + + // Handle password protection + if (share.password && !hasAccess) { + return ; + } + + const isHeaderVisible = + header !== '0' && header !== 0 && header !== 'false' && header !== false; + + return ( +
+ {isHeaderVisible && ( +
+ +
+ )} + +
+
+
+
+ + +
+
+
+
+
+
+
+
{report.name}
+
+
+ +
+
+
+
+
+ ); +} + diff --git a/packages/db/prisma/migrations/20260109144217_add_share_dashboard_and_report/migration.sql b/packages/db/prisma/migrations/20260109144217_add_share_dashboard_and_report/migration.sql new file mode 100644 index 00000000..11d347b4 --- /dev/null +++ b/packages/db/prisma/migrations/20260109144217_add_share_dashboard_and_report/migration.sql @@ -0,0 +1,53 @@ +-- CreateTable +CREATE TABLE "public"."share_dashboards" ( + "id" TEXT NOT NULL, + "dashboardId" TEXT NOT NULL, + "organizationId" TEXT NOT NULL, + "projectId" TEXT NOT NULL, + "public" BOOLEAN NOT NULL DEFAULT false, + "password" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +-- CreateTable +CREATE TABLE "public"."share_reports" ( + "id" TEXT NOT NULL, + "reportId" UUID NOT NULL, + "organizationId" TEXT NOT NULL, + "projectId" TEXT NOT NULL, + "public" BOOLEAN NOT NULL DEFAULT false, + "password" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +-- CreateIndex +CREATE UNIQUE INDEX "share_dashboards_id_key" ON "public"."share_dashboards"("id"); + +-- CreateIndex +CREATE UNIQUE INDEX "share_dashboards_dashboardId_key" ON "public"."share_dashboards"("dashboardId"); + +-- CreateIndex +CREATE UNIQUE INDEX "share_reports_id_key" ON "public"."share_reports"("id"); + +-- CreateIndex +CREATE UNIQUE INDEX "share_reports_reportId_key" ON "public"."share_reports"("reportId"); + +-- AddForeignKey +ALTER TABLE "public"."share_dashboards" ADD CONSTRAINT "share_dashboards_dashboardId_fkey" FOREIGN KEY ("dashboardId") REFERENCES "public"."dashboards"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."share_dashboards" ADD CONSTRAINT "share_dashboards_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."share_dashboards" ADD CONSTRAINT "share_dashboards_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "public"."projects"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."share_reports" ADD CONSTRAINT "share_reports_reportId_fkey" FOREIGN KEY ("reportId") REFERENCES "public"."reports"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."share_reports" ADD CONSTRAINT "share_reports_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."share_reports" ADD CONSTRAINT "share_reports_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "public"."projects"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index b3985854..bafd5f94 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -46,16 +46,18 @@ model Chat { } model Organization { - id String @id @default(dbgenerated("gen_random_uuid()")) + id String @id @default(dbgenerated("gen_random_uuid()")) name String projects Project[] members Member[] createdByUserId String? - createdBy User? @relation(name: "organizationCreatedBy", fields: [createdByUserId], references: [id], onDelete: SetNull) + createdBy User? @relation(name: "organizationCreatedBy", fields: [createdByUserId], references: [id], onDelete: SetNull) ProjectAccess ProjectAccess[] Client Client[] Dashboard Dashboard[] ShareOverview ShareOverview[] + ShareDashboard ShareDashboard[] + ShareReport ShareReport[] integrations Integration[] invites Invite[] timezone String? @@ -185,13 +187,15 @@ model Project { /// [IPrismaProjectFilters] filters Json @default("[]") - clients Client[] - reports Report[] - dashboards Dashboard[] - share ShareOverview? - meta EventMeta[] - references Reference[] - access ProjectAccess[] + clients Client[] + reports Report[] + dashboards Dashboard[] + share ShareOverview? + shareDashboards ShareDashboard[] + shareReports ShareReport[] + meta EventMeta[] + references Reference[] + access ProjectAccess[] notificationRules NotificationRule[] notifications Notification[] @@ -283,13 +287,14 @@ enum ChartType { } model Dashboard { - id String @id @default(dbgenerated("gen_random_uuid()")) + id String @id @default(dbgenerated("gen_random_uuid()")) name String - organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) organizationId String projectId String - project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) reports Report[] + share ShareDashboard? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@ -328,6 +333,7 @@ model Report { dashboardId String dashboard Dashboard @relation(fields: [dashboardId], references: [id], onDelete: Cascade) layout ReportLayout? + share ShareReport? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@ -372,6 +378,38 @@ model ShareOverview { @@map("shares") } +model ShareDashboard { + id String @unique + dashboardId String @unique + dashboard Dashboard @relation(fields: [dashboardId], references: [id], onDelete: Cascade) + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organizationId String + projectId String + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) + public Boolean @default(false) + password String? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + @@map("share_dashboards") +} + +model ShareReport { + id String @unique + reportId String @unique @db.Uuid + report Report @relation(fields: [reportId], references: [id], onDelete: Cascade) + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organizationId String + projectId String + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) + public Boolean @default(false) + password String? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + @@map("share_reports") +} + model EventMeta { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid name String diff --git a/packages/db/src/services/share.service.ts b/packages/db/src/services/share.service.ts index a1149f52..b9b342ee 100644 --- a/packages/db/src/services/share.service.ts +++ b/packages/db/src/services/share.service.ts @@ -18,3 +18,100 @@ export function getShareByProjectId(projectId: string) { }, }); } + +// Dashboard sharing functions +export function getShareDashboardById(id: string) { + return db.shareDashboard.findFirst({ + where: { + id, + }, + include: { + dashboard: { + include: { + project: true, + }, + }, + }, + }); +} + +export function getShareDashboardByDashboardId(dashboardId: string) { + return db.shareDashboard.findUnique({ + where: { + dashboardId, + }, + }); +} + +// Report sharing functions +export function getShareReportById(id: string) { + return db.shareReport.findFirst({ + where: { + id, + }, + include: { + report: { + include: { + project: true, + }, + }, + }, + }); +} + +export function getShareReportByReportId(reportId: string) { + return db.shareReport.findUnique({ + where: { + reportId, + }, + }); +} + +// Validation for secure endpoints +export async function validateReportAccess( + reportId: string, + shareId: string, + shareType: 'dashboard' | 'report', +) { + if (shareType === 'dashboard') { + const share = await db.shareDashboard.findUnique({ + where: { id: shareId }, + include: { + dashboard: { + include: { + reports: { + where: { id: reportId }, + }, + }, + }, + }, + }); + + if (!share || !share.public) { + throw new Error('Share not found or not public'); + } + + if (!share.dashboard.reports.some((r) => r.id === reportId)) { + throw new Error('Report does not belong to this dashboard'); + } + + return share; + } else { + const share = await db.shareReport.findUnique({ + where: { id: shareId }, + include: { + report: true, + }, + }); + + if (!share || !share.public) { + throw new Error('Share not found or not public'); + } + + if (share.reportId !== reportId) { + throw new Error('Report ID mismatch'); + } + + return share; + } +} diff --git a/packages/trpc/src/routers/auth.ts b/packages/trpc/src/routers/auth.ts index ccfc7f7b..fa22019a 100644 --- a/packages/trpc/src/routers/auth.ts +++ b/packages/trpc/src/routers/auth.ts @@ -352,8 +352,23 @@ export const authRouter = createTRPCRouter({ ) .input(zSignInShare) .mutation(async ({ input, ctx }) => { - const { password, shareId } = input; - const share = await getShareOverviewById(input.shareId); + const { password, shareId, shareType = 'overview' } = input; + + let share: { password: string | null; public: boolean } | null = null; + let cookieName = ''; + + if (shareType === 'overview') { + share = await getShareOverviewById(shareId); + cookieName = `shared-overview-${shareId}`; + } else if (shareType === 'dashboard') { + const { getShareDashboardById } = await import('@openpanel/db'); + share = await getShareDashboardById(shareId); + cookieName = `shared-dashboard-${shareId}`; + } else if (shareType === 'report') { + const { getShareReportById } = await import('@openpanel/db'); + share = await getShareReportById(shareId); + cookieName = `shared-report-${shareId}`; + } if (!share) { throw TRPCNotFoundError('Share not found'); @@ -373,7 +388,7 @@ export const authRouter = createTRPCRouter({ throw TRPCAccessError('Incorrect password'); } - ctx.setCookie(`shared-overview-${shareId}`, '1', { + ctx.setCookie(cookieName, '1', { maxAge: 60 * 60 * 24 * 7, ...COOKIE_OPTIONS, }); diff --git a/packages/trpc/src/routers/chart.ts b/packages/trpc/src/routers/chart.ts index e3895d61..d0294a90 100644 --- a/packages/trpc/src/routers/chart.ts +++ b/packages/trpc/src/routers/chart.ts @@ -19,10 +19,12 @@ import { getEventFiltersWhereClause, getEventMetasCached, getProfilesCached, + getReportById, getSelectPropertyKey, getSettingsForProject, onlyReportEvents, sankeyService, + validateReportAccess, } from '@openpanel/db'; import { type IChartEvent, @@ -815,6 +817,397 @@ export const chartRouter = createTRPCRouter({ return profiles; }), + + chartByReport: publicProcedure + .input( + z.object({ + reportId: z.string(), + shareId: z.string(), + shareType: z.enum(['dashboard', 'report']), + range: z.string().optional(), + startDate: z.string().optional(), + endDate: z.string().optional(), + interval: zTimeInterval.optional(), + }), + ) + .query(async ({ input }) => { + // Validate access + await validateReportAccess( + input.reportId, + input.shareId, + input.shareType, + ); + + // Load report from DB + const report = await getReportById(input.reportId); + if (!report) { + throw TRPCAccessError('Report not found'); + } + + // Build chart input from report, merging date overrides + const chartInput: z.infer = { + projectId: report.projectId, + chartType: report.chartType, + series: report.series, + breakdowns: report.breakdowns, + interval: input.interval ?? report.interval, + range: input.range ?? report.range, + startDate: input.startDate ?? null, + endDate: input.endDate ?? null, + previous: report.previous, + formula: report.formula, + metric: report.metric, + }; + + return ChartEngine.execute(chartInput); + }), + + aggregateByReport: publicProcedure + .input( + z.object({ + reportId: z.string(), + shareId: z.string(), + shareType: z.enum(['dashboard', 'report']), + range: z.string().optional(), + startDate: z.string().optional(), + endDate: z.string().optional(), + interval: zTimeInterval.optional(), + }), + ) + .query(async ({ input }) => { + // Validate access + await validateReportAccess( + input.reportId, + input.shareId, + input.shareType, + ); + + // Load report from DB + const report = await getReportById(input.reportId); + if (!report) { + throw TRPCAccessError('Report not found'); + } + + // Build chart input from report, merging date overrides + const chartInput: z.infer = { + projectId: report.projectId, + chartType: report.chartType, + series: report.series, + breakdowns: report.breakdowns, + interval: input.interval ?? report.interval, + range: input.range ?? report.range, + startDate: input.startDate ?? null, + endDate: input.endDate ?? null, + previous: report.previous, + formula: report.formula, + metric: report.metric, + }; + + return AggregateChartEngine.execute(chartInput); + }), + + funnelByReport: publicProcedure + .input( + z.object({ + reportId: z.string(), + shareId: z.string(), + shareType: z.enum(['dashboard', 'report']), + range: z.string().optional(), + startDate: z.string().optional(), + endDate: z.string().optional(), + interval: zTimeInterval.optional(), + }), + ) + .query(async ({ input }) => { + // Validate access + await validateReportAccess( + input.reportId, + input.shareId, + input.shareType, + ); + + // Load report from DB + const report = await getReportById(input.reportId); + if (!report) { + throw TRPCAccessError('Report not found'); + } + + const { timezone } = await getSettingsForProject(report.projectId); + const currentPeriod = getChartStartEndDate( + { + range: input.range ?? report.range, + startDate: input.startDate ?? null, + endDate: input.endDate ?? null, + interval: input.interval ?? report.interval, + }, + timezone, + ); + const previousPeriod = getChartPrevStartEndDate(currentPeriod); + + const [current, previous] = await Promise.all([ + funnelService.getFunnel({ + projectId: report.projectId, + series: report.series, + breakdowns: report.breakdowns, + ...currentPeriod, + timezone, + funnelGroup: report.funnelGroup, + funnelWindow: report.funnelWindow, + }), + report.previous + ? funnelService.getFunnel({ + projectId: report.projectId, + series: report.series, + breakdowns: report.breakdowns, + ...previousPeriod, + timezone, + funnelGroup: report.funnelGroup, + funnelWindow: report.funnelWindow, + }) + : Promise.resolve(null), + ]); + + return { + current, + previous, + }; + }), + + cohortByReport: publicProcedure + .input( + z.object({ + reportId: z.string(), + shareId: z.string(), + shareType: z.enum(['dashboard', 'report']), + range: z.string().optional(), + startDate: z.string().optional(), + endDate: z.string().optional(), + interval: zTimeInterval.optional(), + }), + ) + .query(async ({ input }) => { + // Validate access + await validateReportAccess( + input.reportId, + input.shareId, + input.shareType, + ); + + // Load report from DB + const report = await getReportById(input.reportId); + if (!report) { + throw TRPCAccessError('Report not found'); + } + + const { timezone } = await getSettingsForProject(report.projectId); + const eventSeries = onlyReportEvents(report.series); + const firstEvent = (eventSeries[0]?.filters?.[0]?.value ?? []).map( + String, + ); + const secondEvent = (eventSeries[1]?.filters?.[0]?.value ?? []).map( + String, + ); + + if (firstEvent.length === 0 || secondEvent.length === 0) { + throw new Error('Report must have at least 2 event series'); + } + + const dates = getChartStartEndDate( + { + range: input.range ?? report.range, + startDate: input.startDate ?? null, + endDate: input.endDate ?? null, + interval: input.interval ?? report.interval, + }, + timezone, + ); + const interval = (input.interval ?? report.interval) as + | 'minute' + | 'hour' + | 'day' + | 'week' + | 'month'; + const diffInterval = { + minute: () => differenceInDays(dates.endDate, dates.startDate), + hour: () => differenceInDays(dates.endDate, dates.startDate), + day: () => differenceInDays(dates.endDate, dates.startDate), + week: () => differenceInWeeks(dates.endDate, dates.startDate), + month: () => differenceInMonths(dates.endDate, dates.startDate), + }[interval](); + const sqlInterval = { + minute: 'DAY', + hour: 'DAY', + day: 'DAY', + week: 'WEEK', + month: 'MONTH', + }[interval]; + + const sqlToStartOf = { + minute: 'toDate', + hour: 'toDate', + day: 'toDate', + week: 'toStartOfWeek', + month: 'toStartOfMonth', + }[interval]; + + const countCriteria = + (report.criteria ?? 'on_or_after') === 'on_or_after' ? '>=' : '='; + + const usersSelect = range(0, diffInterval + 1) + .map( + (index) => + `groupUniqArrayIf(profile_id, x_after_cohort ${countCriteria} ${index}) AS interval_${index}_users`, + ) + .join(',\n'); + + const countsSelect = range(0, diffInterval + 1) + .map( + (index) => + `length(interval_${index}_users) AS interval_${index}_user_count`, + ) + .join(',\n'); + + const whereEventNameIs = (event: string[]) => { + if (event.length === 1) { + return `name = ${sqlstring.escape(event[0])}`; + } + return `name IN (${event.map((e) => sqlstring.escape(e)).join(',')})`; + }; + + const cohortQuery = ` + WITH + cohort_users AS ( + SELECT + profile_id AS userID, + project_id, + ${sqlToStartOf}(created_at) AS cohort_interval + FROM ${TABLE_NAMES.cohort_events_mv} + WHERE ${whereEventNameIs(firstEvent)} + AND project_id = ${sqlstring.escape(report.projectId)} + AND created_at BETWEEN toDate('${utc(dates.startDate)}') AND toDate('${utc(dates.endDate)}') + ), + last_event AS + ( + SELECT + profile_id, + project_id, + toDate(created_at) AS event_date + FROM cohort_events_mv + WHERE ${whereEventNameIs(secondEvent)} + AND project_id = ${sqlstring.escape(report.projectId)} + AND created_at BETWEEN toDate('${utc(dates.startDate)}') AND toDate('${utc(dates.endDate)}') + INTERVAL ${diffInterval} ${sqlInterval} + ), + retention_matrix AS + ( + SELECT + f.cohort_interval, + l.profile_id, + dateDiff('${sqlInterval}', f.cohort_interval, ${sqlToStartOf}(l.event_date)) AS x_after_cohort + FROM cohort_users AS f + INNER JOIN last_event AS l ON f.userID = l.profile_id + WHERE (l.event_date >= f.cohort_interval) + AND (l.event_date <= (f.cohort_interval + INTERVAL ${diffInterval} ${sqlInterval})) + ), + interval_users AS ( + SELECT + cohort_interval, + ${usersSelect} + FROM retention_matrix + GROUP BY cohort_interval + ), + cohort_sizes AS ( + SELECT + cohort_interval, + COUNT(DISTINCT userID) AS total_first_event_count + FROM cohort_users + GROUP BY cohort_interval + ) + SELECT + cohort_interval, + cohort_sizes.total_first_event_count, + ${countsSelect} + FROM interval_users + LEFT JOIN cohort_sizes AS cs ON cohort_interval = cs.cohort_interval + ORDER BY cohort_interval ASC + `; + + const cohortData = await chQuery<{ + cohort_interval: string; + total_first_event_count: number; + [key: string]: any; + }>(cohortQuery); + + return processCohortData(cohortData, diffInterval); + }), + + conversionByReport: publicProcedure + .input( + z.object({ + reportId: z.string(), + shareId: z.string(), + shareType: z.enum(['dashboard', 'report']), + range: z.string().optional(), + startDate: z.string().optional(), + endDate: z.string().optional(), + interval: zTimeInterval.optional(), + }), + ) + .query(async ({ input }) => { + // Validate access + await validateReportAccess( + input.reportId, + input.shareId, + input.shareType, + ); + + // Load report from DB + const report = await getReportById(input.reportId); + if (!report) { + throw TRPCAccessError('Report not found'); + } + + const { timezone } = await getSettingsForProject(report.projectId); + const currentPeriod = getChartStartEndDate( + { + range: input.range ?? report.range, + startDate: input.startDate ?? null, + endDate: input.endDate ?? null, + interval: input.interval ?? report.interval, + }, + timezone, + ); + const previousPeriod = getChartPrevStartEndDate(currentPeriod); + + const [current, previous] = await Promise.all([ + conversionService.getConversion({ + projectId: report.projectId, + series: report.series, + breakdowns: report.breakdowns, + ...currentPeriod, + timezone, + }), + report.previous + ? conversionService.getConversion({ + projectId: report.projectId, + series: report.series, + breakdowns: report.breakdowns, + ...previousPeriod, + timezone, + }) + : Promise.resolve(null), + ]); + + return { + current: current.map((serie, sIndex) => ({ + ...serie, + data: serie.data.map((d, dIndex) => ({ + ...d, + previousRate: previous?.[sIndex]?.data?.[dIndex]?.rate, + })), + })), + previous, + }; + }), }); function processCohortData( diff --git a/packages/trpc/src/routers/share.ts b/packages/trpc/src/routers/share.ts index 15f9d8b0..2346111a 100644 --- a/packages/trpc/src/routers/share.ts +++ b/packages/trpc/src/routers/share.ts @@ -1,11 +1,18 @@ import ShortUniqueId from 'short-unique-id'; -import { db } from '@openpanel/db'; -import { zShareOverview } from '@openpanel/validation'; +import { + db, + getReportsByDashboardId, + getReportById, + getShareDashboardById, + getShareReportById, +} from '@openpanel/db'; +import { zShareDashboard, zShareOverview, zShareReport } from '@openpanel/validation'; import { hashPassword } from '@openpanel/auth'; import { z } from 'zod'; -import { TRPCNotFoundError } from '../errors'; +import { getProjectAccess } from '../access'; +import { TRPCAccessError, TRPCNotFoundError } from '../errors'; import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc'; const uid = new ShortUniqueId({ length: 6 }); @@ -85,4 +92,206 @@ export const shareRouter = createTRPCRouter({ }, }); }), + + // Dashboard sharing + dashboard: publicProcedure + .input( + z + .object({ + dashboardId: z.string(), + }) + .or( + z.object({ + shareId: z.string(), + }), + ), + ) + .query(async ({ input, ctx }) => { + const share = await db.shareDashboard.findUnique({ + include: { + organization: { + select: { + name: true, + }, + }, + project: { + select: { + name: true, + }, + }, + dashboard: { + select: { + name: true, + }, + }, + }, + where: + 'dashboardId' in input + ? { + dashboardId: input.dashboardId, + } + : { + id: input.shareId, + }, + }); + + if (!share) { + if ('shareId' in input) { + throw TRPCNotFoundError('Dashboard share not found'); + } + return null; + } + + return { + ...share, + hasAccess: !!ctx.cookies[`shared-dashboard-${share?.id}`], + }; + }), + + createDashboard: protectedProcedure + .input(zShareDashboard) + .mutation(async ({ input, ctx }) => { + const access = await getProjectAccess({ + projectId: input.projectId, + userId: ctx.session.userId, + }); + + if (!access) { + throw TRPCAccessError('You do not have access to this project'); + } + + const passwordHash = input.password + ? await hashPassword(input.password) + : null; + + return db.shareDashboard.upsert({ + where: { + dashboardId: input.dashboardId, + }, + create: { + id: uid.rnd(), + organizationId: input.organizationId, + projectId: input.projectId, + dashboardId: input.dashboardId, + public: input.public, + password: passwordHash, + }, + update: { + public: input.public, + password: passwordHash, + }, + }); + }), + + dashboardReports: publicProcedure + .input( + z.object({ + shareId: z.string(), + }), + ) + .query(async ({ input, ctx }) => { + const share = await getShareDashboardById(input.shareId); + + if (!share || !share.public) { + throw TRPCNotFoundError('Dashboard share not found'); + } + + // Check password access + const hasAccess = !!ctx.cookies[`shared-dashboard-${share.id}`]; + if (share.password && !hasAccess) { + throw TRPCAccessError('Password required'); + } + + return getReportsByDashboardId(share.dashboardId); + }), + + // Report sharing + report: publicProcedure + .input( + z + .object({ + reportId: z.string(), + }) + .or( + z.object({ + shareId: z.string(), + }), + ), + ) + .query(async ({ input, ctx }) => { + const share = await db.shareReport.findUnique({ + include: { + organization: { + select: { + name: true, + }, + }, + project: { + select: { + name: true, + }, + }, + report: { + select: { + name: true, + }, + }, + }, + where: + 'reportId' in input + ? { + reportId: input.reportId, + } + : { + id: input.shareId, + }, + }); + + if (!share) { + if ('shareId' in input) { + throw TRPCNotFoundError('Report share not found'); + } + return null; + } + + return { + ...share, + hasAccess: !!ctx.cookies[`shared-report-${share?.id}`], + }; + }), + + createReport: protectedProcedure + .input(zShareReport) + .mutation(async ({ input, ctx }) => { + const access = await getProjectAccess({ + projectId: input.projectId, + userId: ctx.session.userId, + }); + + if (!access) { + throw TRPCAccessError('You do not have access to this project'); + } + + const passwordHash = input.password + ? await hashPassword(input.password) + : null; + + return db.shareReport.upsert({ + where: { + reportId: input.reportId, + }, + create: { + id: uid.rnd(), + organizationId: input.organizationId, + projectId: input.projectId, + reportId: input.reportId, + public: input.public, + password: passwordHash, + }, + update: { + public: input.public, + password: passwordHash, + }, + }); + }), }); diff --git a/packages/validation/src/index.ts b/packages/validation/src/index.ts index 81f0b9b0..1f97389f 100644 --- a/packages/validation/src/index.ts +++ b/packages/validation/src/index.ts @@ -246,6 +246,22 @@ export const zShareOverview = z.object({ public: z.boolean(), }); +export const zShareDashboard = z.object({ + organizationId: z.string(), + projectId: z.string(), + dashboardId: z.string(), + password: z.string().nullable(), + public: z.boolean(), +}); + +export const zShareReport = z.object({ + organizationId: z.string(), + projectId: z.string(), + reportId: z.string(), + password: z.string().nullable(), + public: z.boolean(), +}); + export const zCreateReference = z.object({ title: z.string(), description: z.string().nullish(), @@ -485,6 +501,7 @@ export type IRequestResetPassword = z.infer; export const zSignInShare = z.object({ password: z.string().min(1), shareId: z.string().min(1), + shareType: z.enum(['overview', 'dashboard', 'report']).optional().default('overview'), }); export type ISignInShare = z.infer; From 347a01a9413e1faea9ab5fba6e6221dbed6ec91b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Sat, 10 Jan 2026 21:55:24 +0100 Subject: [PATCH 5/7] wip 1 --- apps/start/src/components/grafana-grid.tsx | 95 ++ apps/start/src/components/login-navbar.tsx | 2 +- .../components/overview/overview-top-geo.tsx | 3 - .../components/report-chart/area/index.tsx | 40 +- .../src/components/report-chart/bar/index.tsx | 40 +- .../common/previous-diff-indicator.tsx | 4 +- .../src/components/report-chart/context.tsx | 21 +- .../report-chart/conversion/index.tsx | 40 +- .../components/report-chart/funnel/index.tsx | 57 +- .../report-chart/histogram/index.tsx | 40 +- .../components/report-chart/line/index.tsx | 40 +- .../src/components/report-chart/map/index.tsx | 40 +- .../components/report-chart/metric/index.tsx | 40 +- .../report-chart/metric/metric-card.tsx | 7 +- .../src/components/report-chart/pie/index.tsx | 40 +- .../report-chart/retention/index.tsx | 54 +- .../src/components/report/report-item.tsx | 258 ++++++ .../report/sidebar/ReportSettings.tsx | 32 +- apps/start/src/components/ui/label.tsx | 2 +- .../tanstack-query/root-provider.tsx | 40 +- .../src/modals/share-dashboard-modal.tsx | 137 ++- .../start/src/modals/share-overview-modal.tsx | 139 ++- apps/start/src/modals/share-report-modal.tsx | 139 ++- apps/start/src/routes/__root.tsx | 1 - ...Id.$projectId.dashboards_.$dashboardId.tsx | 331 ++----- .../_app.$organizationId.$projectId.pages.tsx | 4 - .../src/routes/share.dashboard.$shareId.tsx | 215 +---- .../src/routes/share.report.$shareId.tsx | 61 +- packages/db/src/services/reports.service.ts | 5 +- packages/db/src/services/share.service.ts | 118 ++- packages/trpc/src/routers/chart.ts | 849 ++++++++---------- packages/trpc/src/routers/share.ts | 16 +- packages/validation/src/index.ts | 9 +- packages/validation/src/test.ts | 28 - packages/validation/src/types.validation.ts | 1 - 35 files changed, 1544 insertions(+), 1404 deletions(-) create mode 100644 apps/start/src/components/grafana-grid.tsx create mode 100644 apps/start/src/components/report/report-item.tsx delete mode 100644 packages/validation/src/test.ts diff --git a/apps/start/src/components/grafana-grid.tsx b/apps/start/src/components/grafana-grid.tsx new file mode 100644 index 00000000..4b4232cc --- /dev/null +++ b/apps/start/src/components/grafana-grid.tsx @@ -0,0 +1,95 @@ +import type { IServiceReport } from '@openpanel/db'; +import { useMemo } from 'react'; +import { Responsive, WidthProvider } from 'react-grid-layout'; + +const ResponsiveGridLayout = WidthProvider(Responsive); + +export type Layout = ReactGridLayout.Layout; + +export const useReportLayouts = ( + reports: NonNullable[], +): ReactGridLayout.Layouts => { + return useMemo(() => { + const baseLayout = reports.map((report, index) => ({ + i: report.id, + x: report.layout?.x ?? (index % 2) * 6, + y: report.layout?.y ?? Math.floor(index / 2) * 4, + w: report.layout?.w ?? 6, + h: report.layout?.h ?? 4, + minW: 3, + minH: 3, + })); + + return { + lg: baseLayout, + md: baseLayout, + sm: baseLayout.map((item) => ({ ...item, w: Math.min(item.w, 6) })), + xs: baseLayout.map((item) => ({ ...item, w: 4, x: 0 })), + xxs: baseLayout.map((item) => ({ ...item, w: 2, x: 0 })), + }; + }, [reports]); +}; + +export function GrafanaGrid({ + layouts, + children, + transitions, + onLayoutChange, + onDragStop, + onResizeStop, + isDraggable, + isResizable, +}: { + children: React.ReactNode; + transitions?: boolean; +} & Pick< + ReactGridLayout.ResponsiveProps, + | 'layouts' + | 'onLayoutChange' + | 'onDragStop' + | 'onResizeStop' + | 'isDraggable' + | 'isResizable' +>) { + return ( + <> + +
+ + {children} + +
+ + ); +} diff --git a/apps/start/src/components/login-navbar.tsx b/apps/start/src/components/login-navbar.tsx index e8cb4770..1c703abb 100644 --- a/apps/start/src/components/login-navbar.tsx +++ b/apps/start/src/components/login-navbar.tsx @@ -33,7 +33,7 @@ export function LoginNavbar({ className }: { className?: string }) {
  • - + Posthog alternative
  • diff --git a/apps/start/src/components/overview/overview-top-geo.tsx b/apps/start/src/components/overview/overview-top-geo.tsx index 1ce23552..ab8a8db5 100644 --- a/apps/start/src/components/overview/overview-top-geo.tsx +++ b/apps/start/src/components/overview/overview-top-geo.tsx @@ -211,7 +211,6 @@ export default function OverviewTopGeo({ projectId }: OverviewTopGeoProps) { void; }[]; }>; - report: IChartProps & { id?: string }; + report: IChartInput & { id?: string }; isLazyLoading: boolean; isEditMode: boolean; shareId?: string; - shareType?: 'dashboard' | 'report'; + reportId?: string; }; type ReportChartContextProviderProps = ReportChartContextType & { @@ -42,8 +41,6 @@ type ReportChartContextProviderProps = ReportChartContextType & { export type ReportChartProps = Partial & { report: IChartInput; lazy?: boolean; - shareId?: string; - shareType?: 'dashboard' | 'report'; }; const context = createContext(null); @@ -58,20 +55,6 @@ export const useReportChartContext = () => { return ctx; }; -export const useSelectReportChartContext = ( - selector: (ctx: ReportChartContextType) => T, -) => { - const ctx = useReportChartContext(); - const [state, setState] = useState(selector(ctx)); - useEffect(() => { - const newState = selector(ctx); - if (!isEqual(newState, state)) { - setState(newState); - } - }, [ctx]); - return state; -}; - export const ReportChartProvider = ({ children, ...propsToContext diff --git a/apps/start/src/components/report-chart/conversion/index.tsx b/apps/start/src/components/report-chart/conversion/index.tsx index fdf94a2d..a594b5cb 100644 --- a/apps/start/src/components/report-chart/conversion/index.tsx +++ b/apps/start/src/components/report-chart/conversion/index.tsx @@ -12,33 +12,27 @@ import { Chart } from './chart'; import { Summary } from './summary'; export function ReportConversionChart() { - const { isLazyLoading, report, shareId, shareType } = useReportChartContext(); + const { isLazyLoading, report, shareId } = useReportChartContext(); const trpc = useTRPC(); const { range, startDate, endDate, interval } = useOverviewOptions(); console.log(report.limit); const res = useQuery( - shareId && shareType && 'id' in report && report.id - ? trpc.chart.conversionByReport.queryOptions( - { - reportId: report.id, - shareId, - shareType, - range: range ?? undefined, - startDate: startDate ?? undefined, - endDate: endDate ?? undefined, - interval: interval ?? undefined, - }, - { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }, - ) - : trpc.chart.conversion.queryOptions(report, { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }), + trpc.chart.conversion.queryOptions( + { + ...report, + shareId, + reportId: 'id' in report ? report.id : undefined, + range: range ?? report.range, + startDate: startDate ?? report.startDate, + endDate: endDate ?? report.endDate, + interval: interval ?? report.interval, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }, + ), ); if ( diff --git a/apps/start/src/components/report-chart/funnel/index.tsx b/apps/start/src/components/report-chart/funnel/index.tsx index e7633239..5b519085 100644 --- a/apps/start/src/components/report-chart/funnel/index.tsx +++ b/apps/start/src/components/report-chart/funnel/index.tsx @@ -25,50 +25,35 @@ export function ReportFunnelChart() { endDate, previous, breakdowns, + interval, }, isLazyLoading, shareId, - shareType, } = useReportChartContext(); const { range: overviewRange, startDate: overviewStartDate, endDate: overviewEndDate, interval: overviewInterval } = useOverviewOptions(); const trpc = useTRPC(); + const input: IChartInput = { + series, + range: overviewRange ?? range, + projectId, + interval: overviewInterval ?? interval ?? 'day', + chartType: 'funnel', + breakdowns, + funnelWindow, + funnelGroup, + previous, + metric: 'sum', + startDate: overviewStartDate ?? startDate, + endDate: overviewEndDate ?? endDate, + limit: 20, + shareId, + reportId: id, + }; const res = useQuery( - shareId && shareType && id - ? trpc.chart.funnelByReport.queryOptions( - { - reportId: id, - shareId, - shareType, - range: overviewRange ?? undefined, - startDate: overviewStartDate ?? undefined, - endDate: overviewEndDate ?? undefined, - interval: overviewInterval ?? undefined, - }, - { - enabled: !isLazyLoading && series.length > 0, - }, - ) - : (() => { - const input: IChartInput = { - series, - range, - projectId, - interval: 'day', - chartType: 'funnel', - breakdowns, - funnelWindow, - funnelGroup, - previous, - metric: 'sum', - startDate, - endDate, - limit: 20, - }; - return trpc.chart.funnel.queryOptions(input, { - enabled: !isLazyLoading && input.series.length > 0, - }); - })(), + trpc.chart.funnel.queryOptions(input, { + enabled: !isLazyLoading && input.series.length > 0, + }), ); if (isLazyLoading || res.isLoading) { diff --git a/apps/start/src/components/report-chart/histogram/index.tsx b/apps/start/src/components/report-chart/histogram/index.tsx index d8092833..e3c2b384 100644 --- a/apps/start/src/components/report-chart/histogram/index.tsx +++ b/apps/start/src/components/report-chart/histogram/index.tsx @@ -10,33 +10,27 @@ import { useReportChartContext } from '../context'; import { Chart } from './chart'; export function ReportHistogramChart() { - const { isLazyLoading, report, shareId, shareType } = useReportChartContext(); + const { isLazyLoading, report, shareId } = useReportChartContext(); const trpc = useTRPC(); const { range, startDate, endDate, interval } = useOverviewOptions(); const res = useQuery( - shareId && shareType && 'id' in report && report.id - ? trpc.chart.chartByReport.queryOptions( - { - reportId: report.id, - shareId, - shareType, - range: range ?? undefined, - startDate: startDate ?? undefined, - endDate: endDate ?? undefined, - interval: interval ?? undefined, - }, - { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }, - ) - : trpc.chart.chart.queryOptions(report, { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }), + trpc.chart.chart.queryOptions( + { + ...report, + shareId, + reportId: 'id' in report ? report.id : undefined, + range: range ?? report.range, + startDate: startDate ?? report.startDate, + endDate: endDate ?? report.endDate, + interval: interval ?? report.interval, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }, + ), ); if ( diff --git a/apps/start/src/components/report-chart/line/index.tsx b/apps/start/src/components/report-chart/line/index.tsx index 111e8458..5b2c90a1 100644 --- a/apps/start/src/components/report-chart/line/index.tsx +++ b/apps/start/src/components/report-chart/line/index.tsx @@ -11,33 +11,27 @@ import { useReportChartContext } from '../context'; import { Chart } from './chart'; export function ReportLineChart() { - const { isLazyLoading, report, shareId, shareType } = useReportChartContext(); + const { isLazyLoading, report, shareId } = useReportChartContext(); const trpc = useTRPC(); const { range, startDate, endDate, interval } = useOverviewOptions(); const res = useQuery( - shareId && shareType && 'id' in report && report.id - ? trpc.chart.chartByReport.queryOptions( - { - reportId: report.id, - shareId, - shareType, - range: range ?? undefined, - startDate: startDate ?? undefined, - endDate: endDate ?? undefined, - interval: interval ?? undefined, - }, - { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }, - ) - : trpc.chart.chart.queryOptions(report, { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }), + trpc.chart.chart.queryOptions( + { + ...report, + shareId, + reportId: 'id' in report ? report.id : undefined, + range: range ?? report.range, + startDate: startDate ?? report.startDate, + endDate: endDate ?? report.endDate, + interval: interval ?? report.interval, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }, + ), ); if ( diff --git a/apps/start/src/components/report-chart/map/index.tsx b/apps/start/src/components/report-chart/map/index.tsx index 7989c07b..8dd256f7 100644 --- a/apps/start/src/components/report-chart/map/index.tsx +++ b/apps/start/src/components/report-chart/map/index.tsx @@ -10,33 +10,27 @@ import { useReportChartContext } from '../context'; import { Chart } from './chart'; export function ReportMapChart() { - const { isLazyLoading, report, shareId, shareType } = useReportChartContext(); + const { isLazyLoading, report, shareId } = useReportChartContext(); const trpc = useTRPC(); const { range, startDate, endDate, interval } = useOverviewOptions(); const res = useQuery( - shareId && shareType && 'id' in report && report.id - ? trpc.chart.chartByReport.queryOptions( - { - reportId: report.id, - shareId, - shareType, - range: range ?? undefined, - startDate: startDate ?? undefined, - endDate: endDate ?? undefined, - interval: interval ?? undefined, - }, - { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }, - ) - : trpc.chart.chart.queryOptions(report, { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }), + trpc.chart.chart.queryOptions( + { + ...report, + shareId, + reportId: 'id' in report ? report.id : undefined, + range: range ?? report.range, + startDate: startDate ?? report.startDate, + endDate: endDate ?? report.endDate, + interval: interval ?? report.interval, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }, + ), ); if ( diff --git a/apps/start/src/components/report-chart/metric/index.tsx b/apps/start/src/components/report-chart/metric/index.tsx index c0b9153f..83447a7b 100644 --- a/apps/start/src/components/report-chart/metric/index.tsx +++ b/apps/start/src/components/report-chart/metric/index.tsx @@ -9,33 +9,27 @@ import { useReportChartContext } from '../context'; import { Chart } from './chart'; export function ReportMetricChart() { - const { isLazyLoading, report, shareId, shareType } = useReportChartContext(); + const { isLazyLoading, report, shareId } = useReportChartContext(); const trpc = useTRPC(); const { range, startDate, endDate, interval } = useOverviewOptions(); const res = useQuery( - shareId && shareType && 'id' in report && report.id - ? trpc.chart.chartByReport.queryOptions( - { - reportId: report.id, - shareId, - shareType, - range: range ?? undefined, - startDate: startDate ?? undefined, - endDate: endDate ?? undefined, - interval: interval ?? undefined, - }, - { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }, - ) - : trpc.chart.chart.queryOptions(report, { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }), + trpc.chart.chart.queryOptions( + { + ...report, + shareId, + reportId: 'id' in report ? report.id : undefined, + range: range ?? report.range, + startDate: startDate ?? report.startDate, + endDate: endDate ?? report.endDate, + interval: interval ?? report.interval, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }, + ), ); if ( diff --git a/apps/start/src/components/report-chart/metric/metric-card.tsx b/apps/start/src/components/report-chart/metric/metric-card.tsx index 7383889f..334091bf 100644 --- a/apps/start/src/components/report-chart/metric/metric-card.tsx +++ b/apps/start/src/components/report-chart/metric/metric-card.tsx @@ -54,10 +54,7 @@ export function MetricCard({ metric, unit, }: MetricCardProps) { - const { - report: { previousIndicatorInverted }, - isEditMode, - } = useReportChartContext(); + const { isEditMode } = useReportChartContext(); const number = useNumber(); const renderValue = (value: number | undefined, unitClassName?: string) => { @@ -80,7 +77,7 @@ export function MetricCard({ const previous = serie.metrics.previous?.[metric]; const graphColors = getDiffIndicator( - previousIndicatorInverted, + false, previous?.state, '#6ee7b7', // green '#fda4af', // red diff --git a/apps/start/src/components/report-chart/pie/index.tsx b/apps/start/src/components/report-chart/pie/index.tsx index dc58942b..7420ac6d 100644 --- a/apps/start/src/components/report-chart/pie/index.tsx +++ b/apps/start/src/components/report-chart/pie/index.tsx @@ -10,33 +10,27 @@ import { useReportChartContext } from '../context'; import { Chart } from './chart'; export function ReportPieChart() { - const { isLazyLoading, report, shareId, shareType } = useReportChartContext(); + const { isLazyLoading, report, shareId } = useReportChartContext(); const trpc = useTRPC(); const { range, startDate, endDate, interval } = useOverviewOptions(); const res = useQuery( - shareId && shareType && 'id' in report && report.id - ? trpc.chart.aggregateByReport.queryOptions( - { - reportId: report.id, - shareId, - shareType, - range: range ?? undefined, - startDate: startDate ?? undefined, - endDate: endDate ?? undefined, - interval: interval ?? undefined, - }, - { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }, - ) - : trpc.chart.aggregate.queryOptions(report, { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: !isLazyLoading, - }), + trpc.chart.aggregate.queryOptions( + { + ...report, + shareId, + reportId: 'id' in report ? report.id : undefined, + range: range ?? report.range, + startDate: startDate ?? report.startDate, + endDate: endDate ?? report.endDate, + interval: interval ?? report.interval, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: !isLazyLoading, + }, + ), ); if ( diff --git a/apps/start/src/components/report-chart/retention/index.tsx b/apps/start/src/components/report-chart/retention/index.tsx index 550b0326..e204a4b5 100644 --- a/apps/start/src/components/report-chart/retention/index.tsx +++ b/apps/start/src/components/report-chart/retention/index.tsx @@ -24,7 +24,6 @@ export function ReportRetentionChart() { }, isLazyLoading, shareId, - shareType, } = useReportChartContext(); const { range: overviewRange, startDate: overviewStartDate, endDate: overviewEndDate, interval: overviewInterval } = useOverviewOptions(); const eventSeries = series.filter((item) => item.type === 'event'); @@ -34,40 +33,25 @@ export function ReportRetentionChart() { firstEvent.length > 0 && secondEvent.length > 0 && !isLazyLoading; const trpc = useTRPC(); const res = useQuery( - shareId && shareType && id - ? trpc.chart.cohortByReport.queryOptions( - { - reportId: id, - shareId, - shareType, - range: overviewRange ?? undefined, - startDate: overviewStartDate ?? undefined, - endDate: overviewEndDate ?? undefined, - interval: overviewInterval ?? undefined, - }, - { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: isEnabled, - }, - ) - : trpc.chart.cohort.queryOptions( - { - firstEvent, - secondEvent, - projectId, - range, - startDate, - endDate, - criteria, - interval, - }, - { - placeholderData: keepPreviousData, - staleTime: 1000 * 60 * 1, - enabled: isEnabled, - }, - ), + trpc.chart.cohort.queryOptions( + { + firstEvent, + secondEvent, + projectId, + range: overviewRange ?? range, + startDate: overviewStartDate ?? startDate, + endDate: overviewEndDate ?? endDate, + criteria, + interval: overviewInterval ?? interval, + shareId, + reportId: id, + }, + { + placeholderData: keepPreviousData, + staleTime: 1000 * 60 * 1, + enabled: isEnabled, + }, + ), ); if (!isEnabled) { diff --git a/apps/start/src/components/report/report-item.tsx b/apps/start/src/components/report/report-item.tsx new file mode 100644 index 00000000..f87fb7f1 --- /dev/null +++ b/apps/start/src/components/report/report-item.tsx @@ -0,0 +1,258 @@ +import { ReportChart } from '@/components/report-chart'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import { cn } from '@/utils/cn'; +import { CopyIcon, MoreHorizontal, Trash } from 'lucide-react'; + +import { timeWindows } from '@openpanel/constants'; + +import { useRouter } from '@tanstack/react-router'; + +export function ReportItemSkeleton() { + return ( +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + ); +} + +export function ReportItem({ + report, + organizationId, + projectId, + range, + startDate, + endDate, + interval, + onDelete, + onDuplicate, +}: { + report: any; + organizationId: string; + projectId: string; + range: any; + startDate: any; + endDate: any; + interval: any; + onDelete: (reportId: string) => void; + onDuplicate: (reportId: string) => void; +}) { + const router = useRouter(); + const chartRange = report.range; + + return ( +
    +
    +
    { + if (event.metaKey) { + window.open( + `/${organizationId}/${projectId}/reports/${report.id}`, + '_blank', + ); + return; + } + router.navigate({ + to: '/$organizationId/$projectId/reports/$reportId', + params: { + organizationId, + projectId, + reportId: report.id, + }, + }); + }} + onKeyUp={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + router.navigate({ + to: '/$organizationId/$projectId/reports/$reportId', + params: { + organizationId, + projectId, + reportId: report.id, + }, + }); + } + }} + role="button" + tabIndex={0} + > +
    {report.name}
    + {chartRange !== null && ( +
    + + {timeWindows[chartRange as keyof typeof timeWindows]?.label} + + {startDate && endDate ? ( + Custom dates + ) : ( + range !== null && + chartRange !== range && ( + + {timeWindows[range as keyof typeof timeWindows]?.label} + + ) + )} +
    + )} +
    +
    +
    + + + + + + + + +
    + + + + + + { + event.stopPropagation(); + onDuplicate(report.id); + }} + > + + Duplicate + + + { + event.stopPropagation(); + onDelete(report.id); + }} + > + + Delete + + + + +
    +
    +
    + +
    +
    + ); +} + +export function ReportItemReadOnly({ + report, + shareId, + range, + startDate, + endDate, + interval, +}: { + report: any; + shareId: string; + range: any; + startDate: any; + endDate: any; + interval: any; +}) { + const chartRange = report.range; + + return ( +
    +
    +
    +
    {report.name}
    + {chartRange !== null && ( +
    + + {timeWindows[chartRange as keyof typeof timeWindows]?.label} + + {startDate && endDate ? ( + Custom dates + ) : ( + range !== null && + chartRange !== range && ( + + {timeWindows[range as keyof typeof timeWindows]?.label} + + ) + )} +
    + )} +
    +
    +
    + +
    +
    + ); +} diff --git a/apps/start/src/components/report/sidebar/ReportSettings.tsx b/apps/start/src/components/report/sidebar/ReportSettings.tsx index 71107152..6ce8f08a 100644 --- a/apps/start/src/components/report/sidebar/ReportSettings.tsx +++ b/apps/start/src/components/report/sidebar/ReportSettings.tsx @@ -67,7 +67,7 @@ export function ReportSettings() { return (

    Settings

    -
    +
    {fields.includes('previous') && (