@sudobility/building_blocks 0.0.21 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -505,9 +505,31 @@ const AppFooter = ({
505
505
  LinkComponent = DefaultLinkComponent$1,
506
506
  sticky = true,
507
507
  isNetworkOnline = true,
508
- className
508
+ className,
509
+ onTrack
509
510
  }) => {
510
511
  const year = copyrightYear || getCopyrightYear$1();
512
+ const track = useCallback(
513
+ (label, params) => {
514
+ onTrack == null ? void 0 : onTrack({
515
+ eventType: "link_click",
516
+ componentName: "AppFooter",
517
+ label,
518
+ params
519
+ });
520
+ },
521
+ [onTrack]
522
+ );
523
+ const createTrackedLinkHandler = useCallback(
524
+ (linkLabel, linkHref, originalOnClick) => (e) => {
525
+ track("footer_link_clicked", {
526
+ link_label: linkLabel,
527
+ link_href: linkHref
528
+ });
529
+ originalOnClick == null ? void 0 : originalOnClick(e);
530
+ },
531
+ [track]
532
+ );
511
533
  const companyLink = companyUrl ? /* @__PURE__ */ jsx(
512
534
  LinkComponent,
513
535
  {
@@ -549,7 +571,11 @@ const AppFooter = ({
549
571
  /* @__PURE__ */ jsx(FooterCompactRight, { children: links.map((link, index) => /* @__PURE__ */ jsx(React.Fragment, { children: link.onClick ? /* @__PURE__ */ jsx(
550
572
  "button",
551
573
  {
552
- onClick: link.onClick,
574
+ onClick: createTrackedLinkHandler(
575
+ link.label,
576
+ link.href,
577
+ link.onClick
578
+ ),
553
579
  className: "text-gray-400 hover:text-white transition-colors",
554
580
  children: link.label
555
581
  }
@@ -557,6 +583,7 @@ const AppFooter = ({
557
583
  LinkComponent,
558
584
  {
559
585
  href: link.href,
586
+ onClick: createTrackedLinkHandler(link.label, link.href),
560
587
  className: "text-gray-400 hover:text-white transition-colors",
561
588
  children: link.label
562
589
  }
@@ -610,10 +637,33 @@ const AppFooterForHomePage = ({
610
637
  LinkComponent = DefaultLinkComponent,
611
638
  isNetworkOnline = true,
612
639
  className,
613
- gridColumns
640
+ gridColumns,
641
+ onTrack
614
642
  }) => {
615
643
  const year = copyrightYear || getCopyrightYear();
616
644
  const gridClass = getGridColumnsClass(linkSections.length, gridColumns);
645
+ const track = useCallback(
646
+ (label, params) => {
647
+ onTrack == null ? void 0 : onTrack({
648
+ eventType: "link_click",
649
+ componentName: "AppFooterForHomePage",
650
+ label,
651
+ params
652
+ });
653
+ },
654
+ [onTrack]
655
+ );
656
+ const createTrackedLinkHandler = useCallback(
657
+ (linkLabel, linkHref, sectionTitle, originalOnClick) => (e) => {
658
+ track("footer_link_clicked", {
659
+ link_label: linkLabel,
660
+ link_href: linkHref,
661
+ section_title: sectionTitle
662
+ });
663
+ originalOnClick == null ? void 0 : originalOnClick(e);
664
+ },
665
+ [track]
666
+ );
617
667
  const companyLink = companyUrl ? /* @__PURE__ */ jsx(
618
668
  LinkComponent,
619
669
  {
@@ -627,7 +677,30 @@ const AppFooterForHomePage = ({
627
677
  FooterLinkSection,
628
678
  {
629
679
  title: section.title,
630
- children: section.links.map((link, linkIndex) => /* @__PURE__ */ jsx(FooterLink, { children: link.onClick ? /* @__PURE__ */ jsx("button", { onClick: link.onClick, className: "text-left", children: link.label }) : /* @__PURE__ */ jsx(LinkComponent, { href: link.href, children: link.label }) }, link.href || linkIndex))
680
+ children: section.links.map((link, linkIndex) => /* @__PURE__ */ jsx(FooterLink, { children: link.onClick ? /* @__PURE__ */ jsx(
681
+ "button",
682
+ {
683
+ onClick: createTrackedLinkHandler(
684
+ link.label,
685
+ link.href,
686
+ section.title,
687
+ link.onClick
688
+ ),
689
+ className: "text-left",
690
+ children: link.label
691
+ }
692
+ ) : /* @__PURE__ */ jsx(
693
+ LinkComponent,
694
+ {
695
+ href: link.href,
696
+ onClick: createTrackedLinkHandler(
697
+ link.label,
698
+ link.href,
699
+ section.title
700
+ ),
701
+ children: link.label
702
+ }
703
+ ) }, link.href || linkIndex))
631
704
  },
632
705
  section.title || sectionIndex
633
706
  )) }),
@@ -895,12 +968,38 @@ const GlobalSettingsPage = ({
895
968
  t,
896
969
  appearanceT,
897
970
  className,
898
- showAppearanceInfoBox = true
971
+ showAppearanceInfoBox = true,
972
+ onTrack
899
973
  }) => {
900
974
  const [selectedSection, setSelectedSection] = useState("appearance");
901
975
  const [mobileView, setMobileView] = useState(
902
976
  "navigation"
903
977
  );
978
+ const track = useCallback(
979
+ (label, params) => {
980
+ onTrack == null ? void 0 : onTrack({
981
+ eventType: "settings_change",
982
+ componentName: "GlobalSettingsPage",
983
+ label,
984
+ params
985
+ });
986
+ },
987
+ [onTrack]
988
+ );
989
+ const handleThemeChange = useCallback(
990
+ (newTheme) => {
991
+ track("theme_changed", { theme: newTheme });
992
+ onThemeChange(newTheme);
993
+ },
994
+ [track, onThemeChange]
995
+ );
996
+ const handleFontSizeChange = useCallback(
997
+ (newFontSize) => {
998
+ track("font_size_changed", { font_size: newFontSize });
999
+ onFontSizeChange(newFontSize);
1000
+ },
1001
+ [track, onFontSizeChange]
1002
+ );
904
1003
  const getText = useCallback(
905
1004
  (key) => {
906
1005
  const fallback = defaultTranslations[key];
@@ -920,8 +1019,8 @@ const GlobalSettingsPage = ({
920
1019
  {
921
1020
  theme,
922
1021
  fontSize,
923
- onThemeChange,
924
- onFontSizeChange,
1022
+ onThemeChange: handleThemeChange,
1023
+ onFontSizeChange: handleFontSizeChange,
925
1024
  t: appearanceT,
926
1025
  showInfoBox: showAppearanceInfoBox
927
1026
  }
@@ -934,18 +1033,20 @@ const GlobalSettingsPage = ({
934
1033
  getText,
935
1034
  theme,
936
1035
  fontSize,
937
- onThemeChange,
938
- onFontSizeChange,
1036
+ handleThemeChange,
1037
+ handleFontSizeChange,
939
1038
  appearanceT,
940
1039
  showAppearanceInfoBox
941
1040
  ]
942
1041
  );
943
1042
  const currentSection = allSections.find((s) => s.id === selectedSection) || allSections[0];
944
1043
  const handleSectionSelect = (sectionId) => {
1044
+ track("section_selected", { section_id: sectionId });
945
1045
  setSelectedSection(sectionId);
946
1046
  setMobileView("content");
947
1047
  };
948
1048
  const handleBackToNavigation = () => {
1049
+ track("back_to_navigation");
949
1050
  setMobileView("navigation");
950
1051
  };
951
1052
  const navigationList = /* @__PURE__ */ jsx("div", { className: "space-y-0", children: allSections.map((section) => {
@@ -4180,13 +4281,15 @@ const DEFAULT_PACKAGE_ENTITLEMENT_MAP = {
4180
4281
  function AppSubscriptionsPage({
4181
4282
  subscription,
4182
4283
  rateLimitsConfig,
4284
+ subscriptionUserId,
4183
4285
  labels,
4184
4286
  formatters,
4185
4287
  packageEntitlementMap = DEFAULT_PACKAGE_ENTITLEMENT_MAP,
4186
4288
  onPurchaseSuccess,
4187
4289
  onRestoreSuccess,
4188
4290
  onError,
4189
- onWarning
4291
+ onWarning,
4292
+ onTrack
4190
4293
  }) {
4191
4294
  const {
4192
4295
  products,
@@ -4201,6 +4304,17 @@ function AppSubscriptionsPage({
4201
4304
  const [selectedPlan, setSelectedPlan] = useState(null);
4202
4305
  const [isPurchasing, setIsPurchasing] = useState(false);
4203
4306
  const [isRestoring, setIsRestoring] = useState(false);
4307
+ const track = useCallback(
4308
+ (label, params) => {
4309
+ onTrack == null ? void 0 : onTrack({
4310
+ eventType: "subscription_action",
4311
+ componentName: "AppSubscriptionsPage",
4312
+ label,
4313
+ params
4314
+ });
4315
+ },
4316
+ [onTrack]
4317
+ );
4204
4318
  useEffect(() => {
4205
4319
  if (error) {
4206
4320
  onError == null ? void 0 : onError(labels.errorTitle, error);
@@ -4212,48 +4326,94 @@ function AppSubscriptionsPage({
4212
4326
  const isYearly = product.period.includes("Y") || product.period.includes("year");
4213
4327
  return billingPeriod === "yearly" ? isYearly : !isYearly;
4214
4328
  }).sort((a, b2) => parseFloat(a.price) - parseFloat(b2.price));
4215
- const handlePeriodChange = (period) => {
4216
- setBillingPeriod(period);
4217
- setSelectedPlan(null);
4218
- };
4219
- const handlePurchase = async () => {
4329
+ const handlePeriodChange = useCallback(
4330
+ (period) => {
4331
+ setBillingPeriod(period);
4332
+ setSelectedPlan(null);
4333
+ track("billing_period_changed", { billing_period: period });
4334
+ },
4335
+ [track]
4336
+ );
4337
+ const handlePurchase = useCallback(async () => {
4220
4338
  if (!selectedPlan) return;
4221
4339
  setIsPurchasing(true);
4222
4340
  clearError();
4341
+ track("purchase_initiated", { plan_identifier: selectedPlan });
4223
4342
  try {
4224
- const result = await purchase(selectedPlan);
4343
+ const result = await purchase(selectedPlan, subscriptionUserId);
4225
4344
  if (result) {
4345
+ track("purchase_completed", { plan_identifier: selectedPlan });
4226
4346
  onPurchaseSuccess == null ? void 0 : onPurchaseSuccess();
4227
4347
  setSelectedPlan(null);
4348
+ } else {
4349
+ track("purchase_failed", {
4350
+ plan_identifier: selectedPlan,
4351
+ reason: "purchase_returned_false"
4352
+ });
4228
4353
  }
4229
4354
  } catch (err) {
4230
- onError == null ? void 0 : onError(
4231
- labels.errorTitle,
4232
- err instanceof Error ? err.message : labels.purchaseError
4233
- );
4355
+ const errorMessage = err instanceof Error ? err.message : labels.purchaseError;
4356
+ track("purchase_failed", {
4357
+ plan_identifier: selectedPlan,
4358
+ reason: errorMessage
4359
+ });
4360
+ onError == null ? void 0 : onError(labels.errorTitle, errorMessage);
4234
4361
  } finally {
4235
4362
  setIsPurchasing(false);
4236
4363
  }
4237
- };
4238
- const handleRestore = async () => {
4364
+ }, [
4365
+ selectedPlan,
4366
+ clearError,
4367
+ track,
4368
+ purchase,
4369
+ subscriptionUserId,
4370
+ onPurchaseSuccess,
4371
+ labels.errorTitle,
4372
+ labels.purchaseError,
4373
+ onError
4374
+ ]);
4375
+ const handlePlanSelect = useCallback(
4376
+ (planIdentifier) => {
4377
+ setSelectedPlan(planIdentifier);
4378
+ track("plan_selected", {
4379
+ plan_identifier: planIdentifier ?? "free",
4380
+ is_free_tier: planIdentifier === null
4381
+ });
4382
+ },
4383
+ [track]
4384
+ );
4385
+ const handleRestore = useCallback(async () => {
4239
4386
  setIsRestoring(true);
4240
4387
  clearError();
4388
+ track("restore_initiated");
4241
4389
  try {
4242
- const result = await restore();
4390
+ const result = await restore(subscriptionUserId);
4243
4391
  if (result) {
4392
+ track("restore_completed");
4244
4393
  onRestoreSuccess == null ? void 0 : onRestoreSuccess();
4245
4394
  } else {
4395
+ track("restore_failed", { reason: "no_purchases_found" });
4246
4396
  onWarning == null ? void 0 : onWarning(labels.errorTitle, labels.restoreNoPurchases);
4247
4397
  }
4248
4398
  } catch (err) {
4249
- onError == null ? void 0 : onError(
4250
- labels.errorTitle,
4251
- err instanceof Error ? err.message : labels.restoreError
4252
- );
4399
+ const errorMessage = err instanceof Error ? err.message : labels.restoreError;
4400
+ track("restore_failed", { reason: errorMessage });
4401
+ onError == null ? void 0 : onError(labels.errorTitle, errorMessage);
4253
4402
  } finally {
4254
4403
  setIsRestoring(false);
4255
4404
  }
4256
- };
4405
+ }, [
4406
+ clearError,
4407
+ track,
4408
+ restore,
4409
+ subscriptionUserId,
4410
+ onRestoreSuccess,
4411
+ labels.errorTitle,
4412
+ labels.restoreNoPurchases,
4413
+ labels.restoreError,
4414
+ onWarning,
4415
+ onError
4416
+ ]);
4257
4417
  const formatExpirationDate = useCallback((date) => {
4258
4418
  if (!date) return "";
4259
4419
  return new Intl.DateTimeFormat(void 0, {
@@ -4479,7 +4639,7 @@ function AppSubscriptionsPage({
4479
4639
  periodLabel: labels.periodMonth,
4480
4640
  features: getFreeTierFeatures(),
4481
4641
  isSelected: !(currentSubscription == null ? void 0 : currentSubscription.isActive) && selectedPlan === null,
4482
- onSelect: () => setSelectedPlan(null),
4642
+ onSelect: () => handlePlanSelect(null),
4483
4643
  topBadge: !(currentSubscription == null ? void 0 : currentSubscription.isActive) ? {
4484
4644
  text: labels.currentPlanBadge,
4485
4645
  color: "green"
@@ -4500,7 +4660,7 @@ function AppSubscriptionsPage({
4500
4660
  periodLabel: getPeriodLabel(product.period),
4501
4661
  features: getProductFeatures(product.identifier),
4502
4662
  isSelected: selectedPlan === product.identifier,
4503
- onSelect: () => setSelectedPlan(product.identifier),
4663
+ onSelect: () => handlePlanSelect(product.identifier),
4504
4664
  isBestValue: product.identifier.includes("pro"),
4505
4665
  discountBadge: ((_a = product.period) == null ? void 0 : _a.includes("Y")) ? (() => {
4506
4666
  const savings = getYearlySavingsPercent(
@@ -4533,9 +4693,43 @@ function AppPricingPage({
4533
4693
  onPlanClick,
4534
4694
  onFreePlanClick,
4535
4695
  faqItems,
4536
- className
4696
+ className,
4697
+ onTrack
4537
4698
  }) {
4538
4699
  const [billingPeriod, setBillingPeriod] = useState("monthly");
4700
+ const track = useCallback(
4701
+ (label, params) => {
4702
+ onTrack == null ? void 0 : onTrack({
4703
+ eventType: "subscription_action",
4704
+ componentName: "AppPricingPage",
4705
+ label,
4706
+ params
4707
+ });
4708
+ },
4709
+ [onTrack]
4710
+ );
4711
+ const handleBillingPeriodChange = useCallback(
4712
+ (value) => {
4713
+ const newPeriod = value;
4714
+ setBillingPeriod(newPeriod);
4715
+ track("billing_period_changed", { billing_period: newPeriod });
4716
+ },
4717
+ [track]
4718
+ );
4719
+ const handleFreePlanClick = useCallback(() => {
4720
+ track("free_plan_clicked", { plan: "free" });
4721
+ onFreePlanClick();
4722
+ }, [track, onFreePlanClick]);
4723
+ const handlePlanClick = useCallback(
4724
+ (planIdentifier, actionType) => {
4725
+ track("plan_clicked", {
4726
+ plan_identifier: planIdentifier,
4727
+ action_type: actionType
4728
+ });
4729
+ onPlanClick(planIdentifier);
4730
+ },
4731
+ [track, onPlanClick]
4732
+ );
4539
4733
  const getProductLevel = useCallback(
4540
4734
  (productId) => {
4541
4735
  const entitlement = entitlementMap[productId];
@@ -4619,7 +4813,7 @@ function AppPricingPage({
4619
4813
  {
4620
4814
  options: billingPeriodOptions,
4621
4815
  value: billingPeriod,
4622
- onChange: (value) => setBillingPeriod(value)
4816
+ onChange: handleBillingPeriodChange
4623
4817
  }
4624
4818
  ) }),
4625
4819
  /* @__PURE__ */ jsxs(
@@ -4652,7 +4846,7 @@ function AppPricingPage({
4652
4846
  // Logged in with subscription: no CTA (can't downgrade here)
4653
4847
  !isAuthenticated ? {
4654
4848
  label: labels.ctaTryFree,
4655
- onClick: onFreePlanClick
4849
+ onClick: handleFreePlanClick
4656
4850
  } : void 0
4657
4851
  ),
4658
4852
  hideSelectionIndicator: isAuthenticated
@@ -4666,14 +4860,14 @@ function AppPricingPage({
4666
4860
  if (!isAuthenticated) {
4667
4861
  ctaButton = {
4668
4862
  label: labels.ctaLogIn,
4669
- onClick: () => onPlanClick(product.identifier)
4863
+ onClick: () => handlePlanClick(product.identifier, "login")
4670
4864
  };
4671
4865
  } else if (isCurrent) {
4672
4866
  ctaButton = void 0;
4673
4867
  } else if (canUpgrade) {
4674
4868
  ctaButton = {
4675
4869
  label: labels.ctaUpgrade,
4676
- onClick: () => onPlanClick(product.identifier)
4870
+ onClick: () => handlePlanClick(product.identifier, "upgrade")
4677
4871
  };
4678
4872
  }
4679
4873
  let topBadge;