@hulkapps/app-manager-vue 3.1.22 → 3.1.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hulkapps/app-manager-vue",
3
- "version": "3.1.22",
3
+ "version": "3.1.25",
4
4
  "description": "Vue SDK to render app manager contents",
5
5
  "main": "dist/app-manager-vue.ssr.js",
6
6
  "browser": "dist/app-manager-vue.esm.js",
@@ -17,6 +17,8 @@ import AppManagerSliderPlan from "./AppManagerSliderPlan";
17
17
  import Vue from "vue";
18
18
  import install from "@/entry.esm";
19
19
 
20
+ const APP_MANAGER_VUE_VERSION = "3.1.24";
21
+
20
22
  export default {
21
23
  name: "AppManagerPlan",
22
24
  components: {AppManagerSliderPlan},
@@ -77,6 +79,10 @@ export default {
77
79
  }
78
80
  install(Vue, config)
79
81
  }
82
+
83
+ console.info("[AppManagerPlan] mounted wrapper context", {
84
+ sdkVersion: APP_MANAGER_VUE_VERSION,
85
+ });
80
86
  }
81
87
  }
82
88
  </script>
@@ -196,6 +196,8 @@ import CustomizationModal from "@/components/PolarisNew/CustomizationModal.vue";
196
196
  import {calculatePlanPriceWithDiscounts} from "@/helpers";
197
197
  import PlanBanners from "@/components/Plans/PlanBanners.vue";
198
198
 
199
+ const APP_MANAGER_VUE_VERSION = "3.1.24";
200
+
199
201
  export default {
200
202
  name: "AppManagerSliderPlan",
201
203
  components: {
@@ -397,7 +399,7 @@ export default {
397
399
  if (this.discount_code !== null) {
398
400
  params['discount_code'] = this.discount_code;
399
401
  }
400
- params['frontend_sdk_version'] = "3.1.22"
402
+ params['frontend_sdk_version'] = APP_MANAGER_VUE_VERSION
401
403
  let {data} = await axios.get(`${this.app_manager_config.baseUrl}/api/app-manager/plans`, {params: params}).catch(error => {
402
404
  console.error(error)
403
405
  });
@@ -445,6 +447,10 @@ export default {
445
447
  if (data.bundle_details) {
446
448
  this.bundle_details = data.bundle_details;
447
449
  }
450
+
451
+ console.info("[AppManagerSliderPlan] plans loaded", {
452
+ sdkVersion: APP_MANAGER_VUE_VERSION,
453
+ });
448
454
  }
449
455
  },
450
456
  handlePlanBannerClose(payload) {
@@ -464,6 +470,9 @@ export default {
464
470
  },
465
471
  async mounted() {
466
472
  this.planLoading = true;
473
+ console.info("[AppManagerSliderPlan] mounting plan view", {
474
+ sdkVersion: APP_MANAGER_VUE_VERSION,
475
+ });
467
476
  await this.fetchFeatures();
468
477
  await this.fetchPlans();
469
478
  this.planLoading = false;
@@ -53,10 +53,6 @@ export default {
53
53
  before: 0,
54
54
  after: 0
55
55
  },
56
- anyMonthlyPlanHasDiscount: false,
57
- anyAnnuallyPlanHasDiscount: false,
58
- anyMonthlyPlanHasNote: false,
59
- anyAnnuallyPlanHasNote: false,
60
56
  loadingPlanId: null
61
57
  };
62
58
  },
@@ -64,38 +60,30 @@ export default {
64
60
  monthlyPlans() {
65
61
  return this.plans
66
62
  .filter(plan => plan.interval === "EVERY_30_DAYS")
67
- .map(plan => {
68
- const planDetails = calculatePlanPriceWithDiscounts(plan, this.promotionalDiscount);
69
- if (planDetails.has_discount && !this.anyMonthlyPlanHasDiscount) {
70
- this.anyMonthlyPlanHasDiscount = true;
71
- }
72
- if (
73
- plan.store_base_plan
74
- && this.anyMonthlyPlanHasNote === false
75
- && isPlanNote(this.shopifyPlan, plan, this.currentPlan, this.hasActiveCharge)
76
- ) {
77
- this.anyMonthlyPlanHasNote = true;
78
- }
79
- return planDetails;
80
- });
63
+ .map(plan => calculatePlanPriceWithDiscounts(plan, this.promotionalDiscount));
81
64
  },
82
65
  annualPlans() {
83
66
  return this.plans
84
67
  .filter(plan => plan.interval === "ANNUAL")
85
- .map(plan => {
86
- const planDetails = calculatePlanPriceWithDiscounts(plan, this.promotionalDiscount);
87
- if (planDetails.has_discount && !this.anyAnnuallyPlanHasDiscount) {
88
- this.anyAnnuallyPlanHasDiscount = true;
89
- }
90
- if (
91
- plan.store_base_plan
92
- && this.anyAnnuallyPlanHasNote === false
93
- && isPlanNote(this.shopifyPlan, plan, this.currentPlan, this.hasActiveCharge)
94
- ) {
95
- this.anyAnnuallyPlanHasNote = true;
96
- }
97
- return planDetails;
98
- });
68
+ .map(plan => calculatePlanPriceWithDiscounts(plan, this.promotionalDiscount));
69
+ },
70
+ anyMonthlyPlanHasDiscount() {
71
+ return this.monthlyPlans.some((plan) => plan.has_discount);
72
+ },
73
+ anyAnnuallyPlanHasDiscount() {
74
+ return this.annualPlans.some((plan) => plan.has_discount);
75
+ },
76
+ anyMonthlyPlanHasNote() {
77
+ return this.monthlyPlans.some((plan) => this.hasPlanNote(plan));
78
+ },
79
+ anyAnnuallyPlanHasNote() {
80
+ return this.annualPlans.some((plan) => this.hasPlanNote(plan));
81
+ },
82
+ anyMonthlyPlanHasDetail() {
83
+ return this.monthlyPlans.some((plan) => this.hasPlanDetail(plan));
84
+ },
85
+ anyAnnuallyPlanHasDetail() {
86
+ return this.annualPlans.some((plan) => this.hasPlanDetail(plan));
99
87
  },
100
88
  isMonthlyVisible() {
101
89
  return this.selectedInterval === "monthly";
@@ -114,8 +102,57 @@ export default {
114
102
  const normalized = Number(value);
115
103
  return Number.isNaN(normalized) ? fallback : normalized;
116
104
  },
105
+ normalizePlanDetailType(type) {
106
+ const normalized = String(type ?? "").trim().toLowerCase();
107
+
108
+ if (normalized === "details") {
109
+ return "detail";
110
+ }
111
+
112
+ if (normalized === "highlights") {
113
+ return "highlight";
114
+ }
115
+
116
+ return normalized;
117
+ },
118
+ hasPlanNote(plan) {
119
+ return isPlanNote(this.shopifyPlan, plan, this.currentPlan, this.hasActiveCharge);
120
+ },
121
+ hasPlanDetail(plan) {
122
+ return this.getSortedPlanDetails(plan).length > 0;
123
+ },
124
+ debugAnyPlanHasDetail(interval) {
125
+ const isMonthlyInterval = interval === "monthly";
126
+ const value = isMonthlyInterval ? this.anyMonthlyPlanHasDetail : this.anyAnnuallyPlanHasDetail;
127
+
128
+ console.info("[PlanCardsHighlights][render] detail-row gate", {
129
+ interval,
130
+ selectedInterval: this.selectedInterval,
131
+ anyMonthlyPlanHasDetail: this.anyMonthlyPlanHasDetail,
132
+ anyAnnuallyPlanHasDetail: this.anyAnnuallyPlanHasDetail,
133
+ value,
134
+ });
135
+
136
+ return value;
137
+ },
138
+ debugPlanDetailVisibility(plan, interval) {
139
+ const primaryDetail = this.getPrimaryPlanDetail(plan);
140
+ const isVisible = this.selectedInterval === interval && Boolean(primaryDetail);
141
+
142
+ console.info("[PlanCardsHighlights][render] detail-row visibility", {
143
+ interval,
144
+ selectedInterval: this.selectedInterval,
145
+ planId: plan?.id,
146
+ hasPrimaryDetail: Boolean(primaryDetail),
147
+ primaryDetail: primaryDetail?.name || null,
148
+ isVisible,
149
+ });
150
+
151
+ return isVisible;
152
+ },
117
153
  getSortedPlanHighlights(plan) {
118
- return Object.values(plan.highlights || {})
154
+ return Object.values(plan.details || {})
155
+ .filter((highlight) => this.normalizePlanDetailType(highlight.type) === "highlight")
119
156
  .sort((a, b) => this.normalizeSortOrder(a.sort_order) - this.normalizeSortOrder(b.sort_order))
120
157
  .map((highlight) => ({
121
158
  plan_id: highlight.plan_id,
@@ -128,6 +165,33 @@ export default {
128
165
  slug: `highlight_${highlight.id}`,
129
166
  }));
130
167
  },
168
+ getSortedPlanDetails(plan) {
169
+ return Object.values(plan.details || {})
170
+ .filter((detail) => this.normalizePlanDetailType(detail.type) === "detail")
171
+ .sort((a, b) => this.normalizeSortOrder(a.sort_order) - this.normalizeSortOrder(b.sort_order))
172
+ .map((detail) => ({
173
+ plan_id: detail.plan_id,
174
+ feature_id: `detail_${detail.id}`,
175
+ uuid: `detail_${detail.id}`,
176
+ value: "1",
177
+ value_type: "boolean",
178
+ name: detail.text,
179
+ format: null,
180
+ slug: `detail_${detail.id}`,
181
+ }));
182
+ },
183
+ getPrimaryPlanDetail(plan) {
184
+ const details = this.getSortedPlanDetails(plan);
185
+ return details.length ? details[0] : null;
186
+ },
187
+ getAdditionalPlanDetails(plan) {
188
+ const details = this.getSortedPlanDetails(plan);
189
+ if (details.length <= 1) {
190
+ return [];
191
+ }
192
+
193
+ return details.slice(1);
194
+ },
131
195
  getSortedPlanFeatures(plan) {
132
196
  const assignedFeatureUuids = new Set(Object.keys(plan.features || {}));
133
197
 
@@ -531,6 +595,37 @@ export default {
531
595
  >
532
596
  {{ translateMe('Note: On account of your recent Shopify plan upgrade, you should consider upgrading your current app plan') }}
533
597
  </p>
598
+ <h6
599
+ class="plan-detail"
600
+ v-if="debugAnyPlanHasDetail('monthly')"
601
+ :style="{
602
+ visibility:
603
+ debugPlanDetailVisibility(plan, 'monthly')
604
+ ? 'visible'
605
+ : 'hidden',
606
+ }"
607
+ >
608
+ <span>
609
+ {{ getPrimaryPlanDetail(plan) ? translateMe(getPrimaryPlanDetail(plan).name) : '' }}
610
+ <span
611
+ v-if="getAdditionalPlanDetails(plan).length"
612
+ class="plan-detail-tooltip-wrapper"
613
+ tabindex="0"
614
+ role="button"
615
+ aria-label="Show more plan details"
616
+ >
617
+ <span class="plan-detail-info">i</span>
618
+ <span class="plan-detail-tooltip">
619
+ <span
620
+ v-for="(detail, detailIndex) in getAdditionalPlanDetails(plan)"
621
+ :key="`monthly_detail_${plan.id}_${detailIndex}`"
622
+ >
623
+ {{ translateMe(detail.name) }}
624
+ </span>
625
+ </span>
626
+ </span>
627
+ </span>
628
+ </h6>
534
629
  </div>
535
630
  </div>
536
631
  </div>
@@ -663,6 +758,37 @@ export default {
663
758
  >
664
759
  {{ translateMe('Note: On account of your recent Shopify plan upgrade, you should consider upgrading your current app plan') }}
665
760
  </p>
761
+ <h6
762
+ class="plan-detail"
763
+ v-if="debugAnyPlanHasDetail('annually')"
764
+ :style="{
765
+ visibility:
766
+ debugPlanDetailVisibility(plan, 'annually')
767
+ ? 'visible'
768
+ : 'hidden',
769
+ }"
770
+ >
771
+ <span>
772
+ {{ getPrimaryPlanDetail(plan) ? translateMe(getPrimaryPlanDetail(plan).name) : '' }}
773
+ <span
774
+ v-if="getAdditionalPlanDetails(plan).length"
775
+ class="plan-detail-tooltip-wrapper"
776
+ tabindex="0"
777
+ role="button"
778
+ aria-label="Show more plan details"
779
+ >
780
+ <span class="plan-detail-info">i</span>
781
+ <span class="plan-detail-tooltip">
782
+ <span
783
+ v-for="(detail, detailIndex) in getAdditionalPlanDetails(plan)"
784
+ :key="`annually_detail_${plan.id}_${detailIndex}`"
785
+ >
786
+ {{ translateMe(detail.name) }}
787
+ </span>
788
+ </span>
789
+ </span>
790
+ </span>
791
+ </h6>
666
792
  </div>
667
793
  </div>
668
794
  </div>
@@ -713,6 +839,8 @@ export default {
713
839
  }
714
840
 
715
841
  .card {
842
+ position: relative;
843
+ z-index: 1;
716
844
  height: 100%;
717
845
  display: flex;
718
846
  flex-direction: column;
@@ -721,6 +849,16 @@ export default {
721
849
  padding: 16px;
722
850
  }
723
851
 
852
+ .swiper-wrapper .swiper-slide {
853
+ position: relative;
854
+ z-index: 1;
855
+ }
856
+
857
+ .swiper-wrapper .swiper-slide:hover,
858
+ .swiper-wrapper .swiper-slide:focus-within {
859
+ z-index: 4;
860
+ }
861
+
724
862
  .swiper-wrapper .swiper-slide .card-border.last-card,
725
863
  .swiper-wrapper .swiper-slide:not(.swiper-slide-active) .card-border {
726
864
  border-left: 1px solid #cccccc;
@@ -730,6 +868,7 @@ export default {
730
868
  font-size: 16px;
731
869
  font-weight: 700;
732
870
  color: #1A1A1A;
871
+ text-align: center;
733
872
  }
734
873
 
735
874
  .card .price-wrapper {
@@ -760,6 +899,7 @@ export default {
760
899
 
761
900
  .card .trial_days,
762
901
  .card .description,
902
+ .card .plan-detail,
763
903
  .card .plan-note {
764
904
  font-size: 13px;
765
905
  font-weight: 400;
@@ -767,11 +907,92 @@ export default {
767
907
  }
768
908
 
769
909
  .card .trial_days,
770
- .card .description {
910
+ .card .description,
911
+ .card .plan-detail {
771
912
  text-align: center;
772
913
  text-transform: unset;
773
914
  }
774
915
 
916
+ .card .plan-detail {
917
+ display: inline-flex;
918
+ align-items: center;
919
+ gap: 6px;
920
+ min-height: 20px;
921
+ width: 100%;
922
+ justify-content: center;
923
+ border-top: 1px solid #cccccc;
924
+ margin-top: auto;
925
+ padding-top: 12px;
926
+ }
927
+
928
+ .plan-detail-info {
929
+ display: inline-flex;
930
+ align-items: center;
931
+ justify-content: center;
932
+ width: 16px;
933
+ height: 16px;
934
+ border-radius: 50%;
935
+ border: 1px solid #757575;
936
+ color: #4A4A4A;
937
+ font-size: 12px;
938
+ line-height: 1;
939
+ cursor: help;
940
+ user-select: none;
941
+ }
942
+
943
+ .plan-detail-tooltip-wrapper {
944
+ position: relative;
945
+ display: inline-flex;
946
+ align-items: center;
947
+ justify-content: center;
948
+ outline: none;
949
+ }
950
+
951
+ .plan-detail-tooltip {
952
+ position: absolute;
953
+ right: -8px;
954
+ bottom: calc(100% + 8px);
955
+ background-color: white;
956
+ color: #333;
957
+ font-weight: 500;
958
+ max-width: 250px;
959
+ min-width: 150px;
960
+ padding: 8px 12px;
961
+ border-radius: 8px;
962
+ font-size: 13px;
963
+ box-shadow: 0 4px 8px #00000026;
964
+ opacity: 0;
965
+ visibility: hidden;
966
+ pointer-events: none;
967
+ transition: opacity 0.2s ease, transform 0.2s ease;
968
+ z-index: 20;
969
+ }
970
+
971
+ .plan-detail-tooltip span {
972
+ display: block;
973
+ text-align: left;
974
+ }
975
+
976
+ .plan-detail-tooltip::after {
977
+ content: '';
978
+ position: absolute;
979
+ top: 100%;
980
+ right: 12px;
981
+ width: 0;
982
+ height: 0;
983
+ border-left: 8px solid transparent;
984
+ border-right: 8px solid transparent;
985
+ border-top: 8px solid white;
986
+ filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.1));
987
+ }
988
+
989
+ .plan-detail-tooltip-wrapper:hover .plan-detail-tooltip,
990
+ .plan-detail-tooltip-wrapper:focus .plan-detail-tooltip,
991
+ .plan-detail-tooltip-wrapper:focus-within .plan-detail-tooltip {
992
+ opacity: 1;
993
+ visibility: visible;
994
+ }
995
+
775
996
  .card .plan-note {
776
997
  margin-top: 8px;
777
998
  text-align: center;
@@ -786,6 +1007,7 @@ export default {
786
1007
 
787
1008
  .features {
788
1009
  margin-top: 19px;
1010
+ margin-bottom: 19px;
789
1011
  }
790
1012
 
791
1013
  .features ul {
@@ -582,7 +582,6 @@ export default {
582
582
  anyMonthlyPlanHasDiscount ? 'has-discount' : ''
583
583
  ]"
584
584
  >
585
- <h4 class="plan-name mobile-plan-name">{{ translateMe(plan.name) }}</h4>
586
585
  <template v-if="plan.strike_price">
587
586
  <h5>
588
587
  <span class="strike-price">${{ Number(plan.strike_price).toFixed(2) }}</span>
@@ -695,7 +694,6 @@ export default {
695
694
  anyAnnuallyPlanHasDiscount ? 'has-discount' : ''
696
695
  ]"
697
696
  >
698
- <h4 class="plan-name mobile-plan-name">{{ translateMe(plan.name) }}</h4>
699
697
  <template v-if="plan.strike_price">
700
698
  <h5>
701
699
  <span class="strike-price">${{ Number(plan.strike_price).toFixed(2) }}</span>
@@ -900,7 +898,7 @@ export default {
900
898
 
901
899
  .plan-header-wrapper .price-wrapper .main-price {
902
900
  display: flex;
903
- flex-direction: row;
901
+ flex-direction: column;
904
902
  gap: 8px;
905
903
  justify-content: center;
906
904
  align-items: center;
@@ -923,15 +921,6 @@ export default {
923
921
  line-height: 20px;
924
922
  font-weight: 700;
925
923
  color: #1A1A1A;
926
- width: max-content;
927
- }
928
-
929
- .plan-header-wrapper .price-wrapper h4.plan-name {
930
- width: 100%;
931
- }
932
-
933
- .plan-header-wrapper .price-wrapper .mobile-plan-name {
934
- display: none;
935
924
  }
936
925
 
937
926
  .plan-header-wrapper .price-wrapper h4 h6 {
@@ -1062,20 +1051,6 @@ export default {
1062
1051
  overscroll-behavior: none !important;
1063
1052
  }
1064
1053
 
1065
- @media (max-width: 1024px) {
1066
- .plan-header-wrapper .price-wrapper .mobile-plan-name {
1067
- display: inline-block;
1068
- }
1069
-
1070
- .plan-header-wrapper .price-wrapper .desktop-plan-name {
1071
- display: none;
1072
- }
1073
-
1074
- .plan-header-wrapper .price-wrapper .main-price {
1075
- flex-direction: column;
1076
- }
1077
- }
1078
-
1079
1054
  @media (max-width: 640px) {
1080
1055
  .swiper-plan-navigation {
1081
1056
  display: none !important;