@feedmepos/mf-connect 0.1.98-beta.13 → 0.1.98-beta.15

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.
Files changed (138) hide show
  1. package/package.json +1 -1
  2. package/dist/App-DQqZfak3.js +0 -4782
  3. package/dist/AuditLogs-C19m85wp.js +0 -2013
  4. package/dist/Bin-GjJ5Q2ae.js +0 -166
  5. package/dist/Broadcast-CVTBL0T0.js +0 -266
  6. package/dist/CampaignTypeDialog.vue_vue_type_script_setup_true_lang-B6BYvVmq.js +0 -120
  7. package/dist/Card-DzYit3v6.js +0 -910
  8. package/dist/ChipMenu.vue_vue_type_script_setup_true_lang-BPpZZNkr.js +0 -97
  9. package/dist/Collections-CaU7auvn.js +0 -540
  10. package/dist/Credit-B_eUMV0R.js +0 -260
  11. package/dist/CustomSelect.vue_vue_type_script_setup_true_lang-Du7fRSZC.js +0 -128
  12. package/dist/DateRangeChip.vue_vue_type_script_setup_true_lang-eIoPtgO5.js +0 -58
  13. package/dist/EditCampaignSheet.vue_vue_type_script_setup_true_lang-Bgcm1S_g.js +0 -3377
  14. package/dist/EffectSheet.vue_vue_type_style_index_0_lang-CZLoCJM4.js +0 -351
  15. package/dist/Experience-B7kxE07l.js +0 -259
  16. package/dist/FileSaver.min-BBvy4uPh.js +0 -74
  17. package/dist/FilterChip.vue_vue_type_script_setup_true_lang-DaU-9GzO.js +0 -129
  18. package/dist/FmTableSelection.vue_vue_type_script_setup_true_lang-DzcUc0_w.js +0 -52
  19. package/dist/Game-B98hcWvR.js +0 -330
  20. package/dist/Grab-RdxV7xhM.js +0 -300
  21. package/dist/ImageInput.vue_vue_type_script_setup_true_lang-BBYekrJp.js +0 -145
  22. package/dist/Index-CvMVxCY5.js +0 -259
  23. package/dist/JsonViewer-v8_C7l5N.js +0 -87
  24. package/dist/LocationPicker.vue_vue_type_script_setup_true_lang-D4-wjMg6.js +0 -48
  25. package/dist/LottieAnimation.vue_vue_type_script_setup_true_lang-Jir8zY8e.js +0 -18737
  26. package/dist/LoyaltyDashboard-B1OzFT4q.js +0 -1082
  27. package/dist/MainLayout-De_edc-D.js +0 -41
  28. package/dist/Marketing-B7-To3XF.js +0 -235
  29. package/dist/MarketingMaterialEdit-DGONncxn.js +0 -1035
  30. package/dist/Member-DEGCrqnh.js +0 -280
  31. package/dist/MemberList-vKYbpLVu.js +0 -1601
  32. package/dist/MemberOverview-wL_ailso.js +0 -58
  33. package/dist/MemberTransactions-CGC3vYKC.js +0 -45
  34. package/dist/MessageInput.vue_vue_type_script_setup_true_lang-BP9YtuPA.js +0 -60802
  35. package/dist/Mission-hxf_AIGL.js +0 -334
  36. package/dist/Overview-NvgUuMYp.js +0 -949
  37. package/dist/Overview-iRLMLoAo.js +0 -1079
  38. package/dist/Point-tkUTXOCe.js +0 -262
  39. package/dist/Promotion-DNUcMKJQ.js +0 -397
  40. package/dist/Queue-5XYiIuG8.js +0 -260
  41. package/dist/Record-B5aYp7eF.js +0 -342
  42. package/dist/Review-BqaHlzXV.js +0 -45
  43. package/dist/ReviewFacebookCallback-BqlFkSB7.js +0 -248
  44. package/dist/ReviewGoogleCallback-ssKL4jh4.js +0 -213
  45. package/dist/Reward-1xOMowTW.js +0 -442
  46. package/dist/RewardGroup.vue_vue_type_script_setup_true_lang-Db3FSfIc.js +0 -149
  47. package/dist/RuleGroup.vue_vue_type_script_setup_true_lang-Cmrp2Tia.js +0 -1911
  48. package/dist/Segment-BMBwsUug.js +0 -1014
  49. package/dist/SelectItems.vue_vue_type_script_setup_true_lang-DSfdei-h.js +0 -181
  50. package/dist/SendCollectionSheet.vue_vue_type_script_setup_true_lang-BJVeGJ5J.js +0 -188
  51. package/dist/SendVoucherSheet.vue_vue_type_script_setup_true_lang-OBZubKUy.js +0 -226
  52. package/dist/Setting-DWygYkHW.js +0 -2047
  53. package/dist/SettingAdvertisements-TYB1b7dB.js +0 -264
  54. package/dist/Settings-DynjmCPL.js +0 -220
  55. package/dist/Store-DRYkmigC.js +0 -1996
  56. package/dist/StoreRewards-DU8eWbXE.js +0 -540
  57. package/dist/StoreTransactions.vue_vue_type_script_setup_true_lang-BkqBjEFm.js +0 -1130
  58. package/dist/TargetLocationInput.vue_vue_type_script_setup_true_lang-l6KmXVBZ.js +0 -7007
  59. package/dist/Tier-4k8SxKtl.js +0 -969
  60. package/dist/Title-B9lWQ_jl.js +0 -566
  61. package/dist/Transaction-C38Yd0tf.js +0 -39
  62. package/dist/UserRuleGroup.vue_vue_type_script_setup_true_lang-D6tgpKpc.js +0 -130
  63. package/dist/Voucher-BQbqC7RV.js +0 -421
  64. package/dist/VoucherEditor.vue_vue_type_script_setup_true_lang-erwpbvYi.js +0 -421
  65. package/dist/ZodTextField.vue_vue_type_script_setup_true_lang-Dt05b7fU.js +0 -56
  66. package/dist/_plugin-vue_export-helper-CHgC5LLL.js +0 -9
  67. package/dist/app-BTm9mz0r.js +0 -59940
  68. package/dist/app-wDQR6dpb.js +0 -39
  69. package/dist/app.js +0 -7
  70. package/dist/business-B7GwHLce.js +0 -82
  71. package/dist/campaign-BCdKDHgz.js +0 -277
  72. package/dist/campaign-CpzFl8Vr.js +0 -4
  73. package/dist/campaign-DWlsbsKQ.js +0 -219
  74. package/dist/campaign.enum-BgK4maXz.js +0 -26
  75. package/dist/campaign.fn-DxnwGCB2.js +0 -776
  76. package/dist/collectible.enum-CdOSUDwG.js +0 -19
  77. package/dist/duplicateCampaign-BSHe0P-g.js +0 -51
  78. package/dist/effect-C7wkN_13.js +0 -89
  79. package/dist/email-addresses-DJycNI7L.js +0 -705
  80. package/dist/export-uIHWDxuh.js +0 -97
  81. package/dist/feedme.ico +0 -0
  82. package/dist/google-BQK68wbe.js +0 -5
  83. package/dist/helper-czpAhAIw.js +0 -86
  84. package/dist/index-5Sa4xS2m.js +0 -151
  85. package/dist/index-5WGEWCcI.js +0 -3
  86. package/dist/index-B2C7kc4z.js +0 -31
  87. package/dist/index-B5eb6_bi.js +0 -3678
  88. package/dist/index-B7nE1Pb2.js +0 -67
  89. package/dist/index-BFAHLexA.js +0 -32
  90. package/dist/index-BanxlNli.js +0 -8910
  91. package/dist/index-ByHoFriu.js +0 -85
  92. package/dist/index-Ca2bBJuR.js +0 -54
  93. package/dist/index-CuvNtC_M.js +0 -61
  94. package/dist/index-D-kYrDAi.js +0 -30289
  95. package/dist/index-D2gAYMIp.js +0 -28900
  96. package/dist/index-DWxmK2NJ.js +0 -61
  97. package/dist/index-U_rwkNKU.js +0 -2819
  98. package/dist/index-knizSLfz.js +0 -1157
  99. package/dist/index-vh86FiIQ.js +0 -10
  100. package/dist/index.esm2017-DFVkiPjd.js +0 -3069
  101. package/dist/loading-B3OgwEC8.js +0 -72
  102. package/dist/lodash-CiRTkvui.js +0 -3678
  103. package/dist/membership-s2VKnu_8.js +0 -255
  104. package/dist/moment-D7p2TMB-.js +0 -2583
  105. package/dist/money-DqxOXYd0.js +0 -15
  106. package/dist/number-Bt4sacaM.js +0 -12
  107. package/dist/object-BYyQ0PZp.js +0 -18
  108. package/dist/objectId-dwaoBfJX.js +0 -7
  109. package/dist/objectid-CxCupSWP.js +0 -145
  110. package/dist/plugins-CQ1JAqJB.js +0 -1788
  111. package/dist/reward-pjbal-XX.js +0 -36
  112. package/dist/rule-CzU2GAur.js +0 -1969
  113. package/dist/string-Gcy5IM_H.js +0 -32
  114. package/dist/style.css +0 -4
  115. package/dist/template-7yiMSL1f.js +0 -188
  116. package/dist/tier-BJDhpH6X.js +0 -4
  117. package/dist/tiers/tier=1.png +0 -0
  118. package/dist/tiers/tier=2.png +0 -0
  119. package/dist/tiers/tier=3.png +0 -0
  120. package/dist/tiers/tier=4.png +0 -0
  121. package/dist/titles/type=diamond.png +0 -0
  122. package/dist/titles/type=drinks.png +0 -0
  123. package/dist/titles/type=food.png +0 -0
  124. package/dist/titles/type=heart.png +0 -0
  125. package/dist/titles/type=medal.png +0 -0
  126. package/dist/titles/type=spend.png +0 -0
  127. package/dist/titles/type=thank-you.png +0 -0
  128. package/dist/titles/type=thumbs-up.png +0 -0
  129. package/dist/titles/type=years.png +0 -0
  130. package/dist/trigger-C5bqlYHn.js +0 -213
  131. package/dist/tslib.es6-BCqqvLQ_.js +0 -44
  132. package/dist/type_drinks-D6jnpvwV.js +0 -4
  133. package/dist/useMarketingMaterialActions-CW_QRtQb.js +0 -107
  134. package/dist/usePagination-D2pke4vy.js +0 -220
  135. package/dist/user-BcXrT_t5.js +0 -55
  136. package/dist/vue-i18n-CFNkj6JK.js +0 -2365
  137. package/dist/xlsx-DyVY5_Qx.js +0 -15491
  138. package/dist/zod-BD_8DRqQ.js +0 -91
@@ -1,1082 +0,0 @@
1
- import { defineComponent as M, resolveComponent as g, openBlock as u, createBlock as C, withCtx as N, createElementVNode as t, createElementBlock as v, Fragment as F, renderList as B, normalizeClass as w, toDisplayString as n, createVNode as b, createCommentVNode as H, createTextVNode as ke, normalizeStyle as Ae, computed as _, unref as y, ref as $, watch as fe, onMounted as Ie } from "vue";
2
- import { h as ye } from "./moment-D7p2TMB-.js";
3
- import { _ as Be } from "./FilterChip.vue_vue_type_script_setup_true_lang-DaU-9GzO.js";
4
- import { _ as be } from "./DateRangeChip.vue_vue_type_script_setup_true_lang-eIoPtgO5.js";
5
- import { u as Ne } from "./loading-B3OgwEC8.js";
6
- import { g as k, S as Te, b as je } from "./index-D2gAYMIp.js";
7
- import { u as Ee } from "./business-B7GwHLce.js";
8
- import { m as z, C as ve } from "./index-knizSLfz.js";
9
- import { useCoreStore as Ve } from "@feedmepos/mf-common";
10
- const Je = { class: "flex flex-col" }, qe = { class: "flex-1 min-w-0" }, Oe = { class: "fm-typo-en-body-sm-600 whitespace-nowrap overflow-hidden text-ellipsis" }, ze = { class: "fm-typo-en-body-xs-400 text-fm-color-typo-tertiary mt-2" }, Ke = { class: "text-right flex-shrink-0" }, Ue = { class: "fm-typo-en-title-xs-800" }, We = { class: "fm-typo-en-body-xs-400 text-fm-color-typo-tertiary mt-2" }, Ye = /* @__PURE__ */ M({
11
- __name: "CampaignActivityList",
12
- props: {
13
- campaigns: {}
14
- },
15
- setup(e) {
16
- return (s, o) => {
17
- const d = g("FmCard");
18
- return u(), C(d, {
19
- variant: "outlined",
20
- class: "p-24"
21
- }, {
22
- default: N(() => [
23
- o[0] || (o[0] = t("div", { class: "flex items-center mb-14" }, [
24
- t("div", { class: "flex-1 fm-typo-en-body-md-600" }, "Recent campaigns"),
25
- t("a", {
26
- href: "#",
27
- class: "fm-typo-en-body-xs-600 text-fm-color-primary no-underline"
28
- }, "All campaigns")
29
- ], -1)),
30
- t("div", Je, [
31
- (u(!0), v(F, null, B(e.campaigns, (a) => (u(), v("div", {
32
- key: a.name,
33
- class: "flex items-start gap-10 py-10 border-b border-fm-color-neutral-gray-100 last:border-b-0 cursor-pointer"
34
- }, [
35
- t("div", {
36
- class: w(["w-7 h-7 rounded-full bg-current flex-shrink-0 mt-5", a.statusClass])
37
- }, null, 2),
38
- t("div", qe, [
39
- t("div", Oe, n(a.name), 1),
40
- t("div", ze, n(a.date) + " · " + n(a.type), 1)
41
- ]),
42
- t("div", Ke, [
43
- t("div", Ue, n(a.metric), 1),
44
- t("div", We, n(a.metricLabel), 1)
45
- ])
46
- ]))), 128))
47
- ])
48
- ]),
49
- _: 1
50
- });
51
- };
52
- }
53
- }), He = { class: "flex items-center gap-8" }, Ge = { class: "w-[30px] h-[30px] rounded-8 bg-[#FFF0E2] flex items-center justify-center flex-shrink-0" }, Qe = { class: "bg-[#FFF6EE] rounded-10 p-14" }, Xe = { class: "fm-typo-en-body-sm-700 mb-6" }, Ze = { class: "fm-typo-en-body-xs-400 text-fm-color-typo-secondary" }, et = { class: "flex flex-col gap-6" }, tt = { class: "border-t border-fm-color-neutral-gray-100 pt-12 flex flex-col gap-7" }, st = { class: "fm-typo-en-body-xs-400 text-fm-color-typo-secondary" }, at = /* @__PURE__ */ M({
54
- __name: "InsightPanel",
55
- props: {
56
- insight: {}
57
- },
58
- emits: ["view-segment"],
59
- setup(e, { emit: s }) {
60
- const o = s;
61
- return (d, a) => {
62
- const l = g("FmIcon"), i = g("FmButton"), c = g("FmCard");
63
- return u(), C(c, {
64
- variant: "outlined",
65
- class: "p-24 flex flex-col gap-14"
66
- }, {
67
- default: N(() => [
68
- t("div", He, [
69
- t("div", Ge, [
70
- b(l, {
71
- name: "info",
72
- outline: "",
73
- size: "sm",
74
- class: "!text-fm-color-primary"
75
- })
76
- ]),
77
- a[1] || (a[1] = t("div", { class: "fm-typo-en-body-md-600" }, "Action insight", -1))
78
- ]),
79
- t("div", Qe, [
80
- t("div", Xe, n(e.insight.title), 1),
81
- t("div", Ze, n(e.insight.body), 1)
82
- ]),
83
- t("div", et, [
84
- a[2] || (a[2] = t("div", { class: "fm-typo-en-body-xs-600 text-fm-color-typo-tertiary uppercase tracking-wide mb-1" }, " Suggested actions ", -1)),
85
- b(i, {
86
- label: e.insight.primaryLabel,
87
- "append-icon": "arrow_forward",
88
- class: "!justify-between"
89
- }, null, 8, ["label"]),
90
- b(i, {
91
- variant: "secondary",
92
- label: e.insight.secondaryLabel,
93
- "append-icon": "arrow_forward",
94
- class: "!justify-between",
95
- onClick: a[0] || (a[0] = (p) => o("view-segment", e.insight.secondaryStage))
96
- }, null, 8, ["label"])
97
- ]),
98
- t("div", tt, [
99
- a[4] || (a[4] = t("div", { class: "fm-typo-en-body-xs-600 text-fm-color-typo-tertiary uppercase tracking-wide mb-1" }, " Other signals ", -1)),
100
- (u(!0), v(F, null, B(e.insight.signals, (p) => (u(), v("div", {
101
- key: p.text,
102
- class: "flex items-start gap-7"
103
- }, [
104
- a[3] || (a[3] = t("div", { class: "w-4 h-4 rounded-full bg-fm-color-neutral-gray-200 flex-shrink-0 mt-6" }, null, -1)),
105
- t("div", st, n(p.text), 1)
106
- ]))), 128))
107
- ])
108
- ]),
109
- _: 1
110
- });
111
- };
112
- }
113
- }), rt = { class: "grid grid-cols-5 xs:grid-cols-2 gap-12" }, ot = { class: "flex items-center gap-8" }, nt = { class: "w-[30px] h-[30px] rounded-8 bg-[#FFF6EE] flex items-center justify-center flex-shrink-0" }, lt = { class: "fm-typo-en-body-lg-600 text-fm-color-typo-secondary flex-1 min-w-0" }, it = { class: "fm-typo-en-title-sm-800 text-fm-color-typo-primary whitespace-nowrap overflow-hidden text-ellipsis" }, ct = {
114
- key: 0,
115
- class: "text-fm-color-typo-success fm-typo-en-body-xs-600 -mt-4"
116
- }, mt = { class: "flex items-center gap-4 flex-wrap" }, dt = /* @__PURE__ */ M({
117
- __name: "KpiStrip",
118
- props: {
119
- kpis: {}
120
- },
121
- setup(e) {
122
- return (s, o) => {
123
- const d = g("FmIcon"), a = g("FmCard");
124
- return u(), v("section", rt, [
125
- (u(!0), v(F, null, B(e.kpis, (l) => (u(), C(a, {
126
- key: l.label,
127
- variant: "outlined",
128
- class: "flex flex-col gap-8 p-16 min-w-0 overflow-hidden"
129
- }, {
130
- default: N(() => [
131
- t("div", ot, [
132
- t("div", nt, [
133
- b(d, {
134
- name: l.icon,
135
- outline: "",
136
- size: "sm",
137
- class: "!text-fm-color-primary"
138
- }, null, 8, ["name"])
139
- ]),
140
- t("span", lt, n(l.label), 1)
141
- ]),
142
- t("div", it, n(l.value), 1),
143
- l.sub ? (u(), v("div", ct, n(l.sub), 1)) : H("", !0),
144
- t("div", mt, [
145
- b(d, {
146
- name: l.trendUp ? "trending_up" : "trending_down",
147
- size: "xs",
148
- class: w(l.deltaClass)
149
- }, null, 8, ["name", "class"]),
150
- t("span", {
151
- class: w(["fm-typo-en-body-xs-600", l.deltaClass])
152
- }, n(l.delta), 3),
153
- o[0] || (o[0] = t("span", { class: "fm-typo-en-body-xs-400 text-fm-color-typo-tertiary" }, "vs prev", -1))
154
- ])
155
- ]),
156
- _: 2
157
- }, 1024))), 128))
158
- ]);
159
- };
160
- }
161
- }), ut = { class: "flex items-center mb-16" }, pt = { class: "flex flex-col gap-9" }, ft = ["onClick"], yt = { class: "fm-typo-en-title-xs-800 min-w-[38px] text-right" }, bt = { class: "fm-typo-en-body-xs-500 text-fm-color-typo-tertiary min-w-[34px] text-right" }, vt = { class: "mt-14 pt-12 border-t border-fm-color-neutral-gray-100 fm-typo-en-body-xs-400 text-fm-color-typo-secondary" }, gt = /* @__PURE__ */ M({
162
- __name: "LifecycleSegments",
163
- props: {
164
- segments: {},
165
- summary: {},
166
- interactive: { type: Boolean, default: !0 }
167
- },
168
- emits: ["select"],
169
- setup(e, { emit: s }) {
170
- const o = e, d = s;
171
- return (a, l) => {
172
- const i = g("FmButton"), c = g("FmProgressBar"), p = g("FmCard");
173
- return u(), C(p, {
174
- variant: "outlined",
175
- class: "p-24"
176
- }, {
177
- default: N(() => [
178
- t("div", ut, [
179
- l[0] || (l[0] = t("div", { class: "flex-1 fm-typo-en-body-md-600" }, "Member lifecycle", -1)),
180
- b(i, {
181
- variant: "secondary",
182
- size: "sm",
183
- label: "Create campaign"
184
- })
185
- ]),
186
- t("div", pt, [
187
- (u(!0), v(F, null, B(o.segments, (m) => (u(), v("div", {
188
- key: m.id,
189
- class: w(["flex items-center gap-9 px-6 py-3 -mx-6 rounded-8", o.interactive ? "cursor-pointer hover:bg-fm-color-neutral-gray-100/50" : "cursor-default"]),
190
- onClick: (S) => o.interactive && d("select", m.id)
191
- }, [
192
- t("span", {
193
- class: w(["fm-typo-en-body-xs-600 w-[90px] flex-shrink-0", m.labelClass])
194
- }, n(m.label), 3),
195
- b(c, {
196
- class: "flex-1",
197
- value: m.barPct,
198
- color: m.barColor,
199
- size: "md"
200
- }, null, 8, ["value", "color"]),
201
- t("span", yt, n(m.countStr), 1),
202
- t("span", bt, n(m.pctStr), 1)
203
- ], 10, ft))), 128))
204
- ]),
205
- t("div", vt, n(o.summary), 1)
206
- ]),
207
- _: 1
208
- });
209
- };
210
- }
211
- }), xt = { class: "flex items-center gap-12 mb-16" }, ht = { class: "flex-1" }, _t = { class: "fm-typo-en-body-md-600" }, wt = { class: "fm-typo-en-body-xs-400 text-fm-color-typo-tertiary mt-1" }, $t = { class: "flex items-center gap-8 min-w-0" }, St = { class: "w-[28px] h-[28px] rounded-full bg-[#FFF0E2] flex items-center justify-center fm-typo-en-body-xs-700 text-fm-color-primary flex-shrink-0" }, Ft = { class: "min-w-0" }, Mt = { class: "fm-typo-en-body-sm-600 whitespace-nowrap overflow-hidden text-ellipsis" }, Ct = { class: "fm-typo-en-body-xs-400 text-fm-color-typo-tertiary whitespace-nowrap overflow-hidden text-ellipsis" }, Rt = { class: "fm-typo-en-body-sm-400 text-fm-color-typo-secondary" }, Pt = { class: "fm-typo-en-body-sm-400 text-fm-color-typo-secondary" }, Lt = { class: "fm-typo-en-body-sm-600" }, Dt = { class: "pt-14 flex items-center justify-center gap-4 fm-typo-en-body-xs-400 text-fm-color-typo-tertiary" }, ge = "grid-cols-[1fr_108px_130px_76px_88px]", kt = /* @__PURE__ */ M({
212
- __name: "MemberDrillDownTable",
213
- props: {
214
- title: {},
215
- countLabel: {},
216
- members: {}
217
- },
218
- emits: ["close"],
219
- setup(e, { emit: s }) {
220
- const o = s;
221
- return (d, a) => {
222
- const l = g("FmButton"), i = g("FmCard");
223
- return u(), C(i, {
224
- variant: "outlined",
225
- class: "p-24"
226
- }, {
227
- default: N(() => [
228
- t("div", xt, [
229
- t("div", ht, [
230
- t("div", _t, n(e.title), 1),
231
- t("div", wt, n(e.countLabel), 1)
232
- ]),
233
- b(l, {
234
- variant: "secondary",
235
- size: "sm",
236
- "prepend-icon": "close",
237
- label: "Close",
238
- onClick: a[0] || (a[0] = (c) => o("close"))
239
- })
240
- ]),
241
- t("div", {
242
- class: w(["grid gap-8 p-8 bg-[#FAFAFA] rounded-8 mb-2", ge])
243
- }, [...a[1] || (a[1] = [
244
- t("span", { class: "fm-typo-en-body-xs-600 text-fm-color-typo-tertiary uppercase tracking-wide" }, "Member", -1),
245
- t("span", { class: "fm-typo-en-body-xs-600 text-fm-color-typo-tertiary uppercase tracking-wide" }, "Joined", -1),
246
- t("span", { class: "fm-typo-en-body-xs-600 text-fm-color-typo-tertiary uppercase tracking-wide" }, "Last active", -1),
247
- t("span", { class: "fm-typo-en-body-xs-600 text-fm-color-typo-tertiary uppercase tracking-wide" }, "Points", -1),
248
- t("span", { class: "fm-typo-en-body-xs-600 text-fm-color-typo-tertiary uppercase tracking-wide" }, "Status", -1)
249
- ])], 2),
250
- (u(!0), v(F, null, B(e.members, (c) => (u(), v("div", {
251
- key: c.email,
252
- class: w(["grid gap-8 py-12 px-8 border-b border-fm-color-neutral-gray-100 items-center", ge])
253
- }, [
254
- t("div", $t, [
255
- t("div", St, n(c.initials), 1),
256
- t("div", Ft, [
257
- t("div", Mt, n(c.name), 1),
258
- t("div", Ct, n(c.email), 1)
259
- ])
260
- ]),
261
- t("span", Rt, n(c.joined), 1),
262
- t("span", Pt, n(c.lastActive), 1),
263
- t("span", Lt, n(c.points), 1),
264
- t("span", {
265
- class: w(["fm-typo-en-body-xs-600 px-9 py-3 rounded-full inline-block whitespace-nowrap w-fit", c.statusClass])
266
- }, n(c.status), 3)
267
- ], 2))), 128)),
268
- t("div", Dt, [
269
- ke(n(e.countLabel) + " · ", 1),
270
- a[2] || (a[2] = t("a", {
271
- href: "#",
272
- class: "text-fm-color-primary fm-typo-en-body-xs-600 no-underline ml-4"
273
- }, "View all in Members", -1))
274
- ])
275
- ]),
276
- _: 1
277
- });
278
- };
279
- }
280
- }), At = { class: "flex items-start gap-8 mb-20" }, It = { class: "flex-1" }, Bt = { class: "fm-typo-en-body-sm-400 text-fm-color-typo-tertiary mt-2" }, Nt = {
281
- key: 0,
282
- class: "flex items-start pl-[150px] ml-12"
283
- }, Tt = { class: "flex flex-col items-start gap-3 py-1" }, jt = { class: "flex items-center gap-6" }, Et = {
284
- key: 0,
285
- class: "fm-typo-en-body-xs-600 text-fm-color-typo-warning bg-[#FFF0E2] px-10 py-3 rounded-full whitespace-nowrap"
286
- }, Vt = ["onClick"], Jt = { class: "w-[140px] flex-shrink-0" }, qt = { class: "fm-typo-en-body-sm-600" }, Ot = { class: "fm-typo-en-body-sm-400 text-fm-color-typo-secondary mt-1" }, zt = { class: "w-[72px] text-right flex-shrink-0" }, Kt = { class: "fm-typo-en-title-xs-800" }, Ut = {
287
- key: 0,
288
- class: "fm-typo-en-body-xs-500 text-fm-color-typo-tertiary"
289
- }, Wt = { class: "mt-14 pt-12 border-t border-fm-color-neutral-gray-100" }, Yt = { class: "fm-typo-en-body-sm-400 text-fm-color-typo-tertiary text-right" }, Ht = /* @__PURE__ */ M({
290
- __name: "MemberFunnel",
291
- props: {
292
- stages: {},
293
- footnote: {},
294
- interactive: { type: Boolean, default: !0 }
295
- },
296
- emits: ["select"],
297
- setup(e, { emit: s }) {
298
- const o = e, d = s;
299
- function a(l) {
300
- o.interactive && d("select", l);
301
- }
302
- return (l, i) => {
303
- const c = g("FmProgressBar"), p = g("FmCard");
304
- return u(), C(p, {
305
- variant: "outlined",
306
- class: "p-24"
307
- }, {
308
- default: N(() => [
309
- t("div", At, [
310
- t("div", It, [
311
- i[0] || (i[0] = t("div", { class: "fm-typo-en-body-md-600" }, "Member funnel", -1)),
312
- t("div", Bt, n(o.interactive ? "Strict member-only · visitors excluded · click any stage to drill down" : "Strict member-only · visitors excluded"), 1)
313
- ])
314
- ]),
315
- (u(!0), v(F, null, B(e.stages, (m, S) => (u(), v("div", {
316
- key: m.id
317
- }, [
318
- m.hasDropLine ? (u(), v("div", Nt, [
319
- t("div", Tt, [
320
- i[1] || (i[1] = t("div", { class: "w-[1px] h-[10px] bg-fm-color-neutral-gray-100 ml-[9px]" }, null, -1)),
321
- t("div", jt, [
322
- t("span", {
323
- class: w(["fm-typo-en-body-xs-600 px-10 py-3 rounded-full whitespace-nowrap", m.dropClass])
324
- }, n(m.conversionStr), 3),
325
- m.isBiggestDrop ? (u(), v("span", Et, " biggest drop ")) : H("", !0)
326
- ]),
327
- i[2] || (i[2] = t("div", { class: "w-[1px] h-[10px] bg-fm-color-neutral-gray-100 ml-[9px]" }, null, -1))
328
- ])
329
- ])) : H("", !0),
330
- t("div", {
331
- class: w(["flex items-center gap-10 py-7 px-10 -mx-10 rounded-10", o.interactive ? "cursor-pointer hover:bg-fm-color-neutral-gray-100/50" : ""]),
332
- onClick: (R) => a(m.id)
333
- }, [
334
- t("div", Jt, [
335
- t("div", qt, n(m.label), 1),
336
- t("div", Ot, n(m.desc), 1)
337
- ]),
338
- b(c, {
339
- class: "flex-1",
340
- value: m.barWidthPct,
341
- color: m.barColor,
342
- size: "lg"
343
- }, null, 8, ["value", "color"]),
344
- t("div", zt, [
345
- t("div", Kt, n(m.countStr), 1),
346
- m.hasConvRate ? (u(), v("div", Ut, n(m.convRateLabel), 1)) : H("", !0)
347
- ])
348
- ], 10, Vt)
349
- ]))), 128)),
350
- t("div", Wt, [
351
- t("div", Yt, n(e.footnote), 1)
352
- ])
353
- ]),
354
- _: 1
355
- });
356
- };
357
- }
358
- }), Gt = { class: "grid grid-cols-3 gap-10 mb-16" }, Qt = { class: "fm-typo-en-body-xs-500 text-fm-color-typo-secondary mb-4" }, Xt = { class: "fm-typo-en-title-xs-800" }, Zt = { class: "flex flex-col gap-9" }, es = { class: "flex-1 fm-typo-en-body-sm-400 whitespace-nowrap overflow-hidden text-ellipsis" }, ts = { class: "fm-typo-en-body-xs-600 text-fm-color-typo-secondary flex-shrink-0" }, ss = { class: "w-[48px] h-4 bg-fm-color-neutral-gray-100 rounded-2 overflow-hidden flex-shrink-0" }, as = /* @__PURE__ */ M({
359
- __name: "RewardPerformance",
360
- props: {
361
- kpis: {},
362
- topRewards: {}
363
- },
364
- setup(e) {
365
- return (s, o) => {
366
- const d = g("FmCard");
367
- return u(), C(d, {
368
- variant: "outlined",
369
- class: "p-24"
370
- }, {
371
- default: N(() => [
372
- o[1] || (o[1] = t("div", { class: "flex items-center mb-16" }, [
373
- t("div", { class: "flex-1 fm-typo-en-body-md-600" }, "Reward performance"),
374
- t("a", {
375
- href: "#",
376
- class: "fm-typo-en-body-xs-600 text-fm-color-primary no-underline"
377
- }, "All rewards")
378
- ], -1)),
379
- t("div", Gt, [
380
- (u(!0), v(F, null, B(e.kpis, (a) => (u(), v("div", {
381
- key: a.label,
382
- class: "bg-[#FAFAFA] rounded-10 p-12"
383
- }, [
384
- t("div", Qt, n(a.label), 1),
385
- t("div", Xt, n(a.value), 1),
386
- t("div", {
387
- class: w(["fm-typo-en-body-xs-600 mt-3", a.deltaClass])
388
- }, n(a.delta), 3)
389
- ]))), 128))
390
- ]),
391
- o[2] || (o[2] = t("div", { class: "fm-typo-en-body-xs-600 text-fm-color-typo-tertiary uppercase tracking-wide mb-10" }, " Top redeemed ", -1)),
392
- t("div", Zt, [
393
- (u(!0), v(F, null, B(e.topRewards, (a) => (u(), v("div", {
394
- key: a.name,
395
- class: "flex items-center gap-9"
396
- }, [
397
- o[0] || (o[0] = t("div", { class: "w-6 h-6 rounded-2 bg-fm-color-primary flex-shrink-0" }, null, -1)),
398
- t("span", es, n(a.name), 1),
399
- t("span", ts, n(a.count), 1),
400
- t("div", ss, [
401
- t("div", {
402
- class: "h-full bg-fm-color-primary rounded-2",
403
- style: Ae({ width: a.barPct + "%" })
404
- }, null, 4)
405
- ])
406
- ]))), 128))
407
- ])
408
- ]),
409
- _: 1
410
- });
411
- };
412
- }
413
- }), rs = { class: "flex items-baseline gap-10 mb-14 flex-wrap" }, os = { class: "fm-typo-en-title-lg-800" }, ns = /* @__PURE__ */ M({
414
- __name: "SalesTrendChart",
415
- props: {
416
- sales: {}
417
- },
418
- setup(e) {
419
- const s = e, o = _(() => [
420
- {
421
- label: "This period",
422
- data: s.sales.series,
423
- color: "#FF7823",
424
- fill: !0
425
- },
426
- {
427
- label: "Prev. period",
428
- data: s.sales.prevSeries,
429
- color: "#B2B3B6",
430
- dash: !0
431
- }
432
- ]), d = { display: !1 };
433
- return (a, l) => {
434
- const i = g("FmLineChart"), c = g("FmCard");
435
- return u(), C(c, {
436
- variant: "outlined",
437
- class: "p-24"
438
- }, {
439
- default: N(() => [
440
- l[1] || (l[1] = t("div", { class: "fm-typo-en-body-md-600 mb-6" }, "Member sales trend", -1)),
441
- t("div", rs, [
442
- t("span", os, n(e.sales.totalFmt), 1),
443
- t("span", {
444
- class: w(["fm-typo-en-body-xs-600 px-9 py-3 rounded-full whitespace-nowrap", e.sales.deltaClass])
445
- }, n(e.sales.deltaStr), 3),
446
- l[0] || (l[0] = t("span", { class: "fm-typo-en-body-xs-400 text-fm-color-typo-tertiary" }, "vs prev. period", -1))
447
- ]),
448
- b(i, {
449
- datasets: y(o),
450
- "x-axis": { categories: e.sales.categories },
451
- "x-cfg": d,
452
- "y-cfg": d,
453
- legend: { display: !1 },
454
- height: "80px"
455
- }, null, 8, ["datasets", "x-axis"]),
456
- l[2] || (l[2] = t("div", { class: "flex items-center gap-14 mt-10" }, [
457
- t("div", { class: "flex items-center gap-5" }, [
458
- t("div", { class: "w-14 h-2 bg-fm-color-primary rounded-1" }),
459
- t("span", { class: "fm-typo-en-body-xs-400 text-fm-color-typo-secondary" }, "This period")
460
- ]),
461
- t("div", { class: "flex items-center gap-5" }, [
462
- t("div", { class: "w-14 border-t-[1.5px] border-dashed border-fm-color-neutral-gray-200" }),
463
- t("span", { class: "fm-typo-en-body-xs-400 text-fm-color-typo-secondary" }, "Prev. period")
464
- ])
465
- ], -1))
466
- ]),
467
- _: 1
468
- });
469
- };
470
- }
471
- }), A = () => {
472
- const e = Te.getBusinessId();
473
- if (!e) throw new Error("Invalid business ID");
474
- return je(`businesses/${e}/loyalty-dashboard`, {
475
- noIdRequired: !0
476
- });
477
- }, I = {
478
- async readTotalMembers(e) {
479
- return k(
480
- await A().post(
481
- "metrics/total-members",
482
- e
483
- )
484
- );
485
- },
486
- async readActiveMembers(e) {
487
- return k(
488
- await A().post(
489
- "metrics/active-members",
490
- e
491
- )
492
- );
493
- },
494
- async readMemberSales(e) {
495
- return k(
496
- await A().post("metrics/member-sales", e)
497
- );
498
- },
499
- async readMemberSalesTrend(e) {
500
- return k(
501
- await A().post(
502
- "metrics/member-sales-trend",
503
- e
504
- )
505
- );
506
- },
507
- async readRepeatRate(e) {
508
- return k(
509
- await A().post("metrics/repeat-rate", e)
510
- );
511
- },
512
- async readVoucherRedemptionRate(e) {
513
- return k(
514
- await A().post(
515
- "metrics/voucher-redemption-rate",
516
- e
517
- )
518
- );
519
- },
520
- async readMemberFunnel(e) {
521
- return k(
522
- await A().post(
523
- "metrics/member-funnel",
524
- e
525
- )
526
- );
527
- },
528
- async readMemberLifecycle(e) {
529
- return k(
530
- await A().post(
531
- "metrics/member-lifecycle",
532
- e
533
- )
534
- );
535
- }
536
- };
537
- function ls(e, s, o = /* @__PURE__ */ new Date()) {
538
- const d = z.tz(o, s).endOf("day"), a = d.clone().startOf("day");
539
- return a.startOf("month"), [a.toISOString(), d.toISOString()];
540
- }
541
- function is(e, s) {
542
- return e.length ? e : s;
543
- }
544
- function cs(e) {
545
- return {
546
- dateRange: e.dateRange,
547
- restaurantIds: is(
548
- e.selectedLocationIds,
549
- e.locationIds
550
- ),
551
- timezone: e.timezone
552
- };
553
- }
554
- function ms(e) {
555
- if (!e.startDate || !e.endDate) return "this-month";
556
- const s = z(e.endDate).diff(z(e.startDate), "days") + 1;
557
- return s <= 10 ? "last-7" : s <= 35 ? "this-month" : s <= 100 ? "last-30" : "last-quarter";
558
- }
559
- function xe(e) {
560
- const [s, o] = e, d = z(o).diff(z(s)), a = z(s).subtract(1, "millisecond");
561
- return [a.clone().subtract(d, "milliseconds").toISOString(), a.toISOString()];
562
- }
563
- const ae = {
564
- "this-month": {
565
- total: 12847,
566
- totalNew: 234,
567
- totalPrev: 12613,
568
- active: 4621,
569
- activePrev: 4789,
570
- purchasing: 2941,
571
- purchasingPrev: 2841,
572
- repeat: 1176,
573
- repeatPrev: 1089,
574
- redeemers: 353,
575
- redeemersPrev: 420,
576
- memberSales: 84320,
577
- memberSalesPrev: 77814,
578
- series: [2100, 2400, 2200, 2800, 3100, 2700, 3200, 3600, 3100, 3900, 4200, 3800, 4100, 4600, 4300, 4800, 5200, 4900, 5400, 5100],
579
- prev: [1800, 2100, 1900, 2400, 2700, 2300, 2800, 3100, 2700, 3400, 3700, 3300, 3600, 4100, 3800, 4300, 4600, 4200, 4800, 4500]
580
- },
581
- "last-7": {
582
- total: 12847,
583
- totalNew: 52,
584
- totalPrev: 12795,
585
- active: 1840,
586
- activePrev: 1910,
587
- purchasing: 1102,
588
- purchasingPrev: 1089,
589
- repeat: 441,
590
- repeatPrev: 412,
591
- redeemers: 128,
592
- redeemersPrev: 152,
593
- memberSales: 19240,
594
- memberSalesPrev: 21300,
595
- series: [2600, 2400, 2800, 2200, 3100, 2900, 3200],
596
- prev: [2900, 2700, 3100, 2500, 3300, 3100, 3500]
597
- },
598
- "last-30": {
599
- total: 12847,
600
- totalNew: 680,
601
- totalPrev: 12167,
602
- active: 5840,
603
- activePrev: 5420,
604
- purchasing: 3610,
605
- purchasingPrev: 3240,
606
- repeat: 1446,
607
- repeatPrev: 1296,
608
- redeemers: 462,
609
- redeemersPrev: 480,
610
- memberSales: 112400,
611
- memberSalesPrev: 98600,
612
- series: [2100, 2400, 2200, 2800, 3100, 2700, 3200, 3600, 3100, 3900, 4200, 3800, 4100, 4600, 4300, 4800, 5200, 4900, 5400, 5100, 4800, 5300, 5600, 5100, 5800, 5500, 5900, 6200, 5800, 6400],
613
- prev: [1800, 2100, 1900, 2400, 2700, 2300, 2800, 3100, 2700, 3400, 3700, 3300, 3600, 4100, 3800, 4300, 4600, 4200, 4800, 4500, 4200, 4700, 5e3, 4600, 5200, 4900, 5300, 5600, 5200, 5800]
614
- },
615
- "last-quarter": {
616
- total: 12847,
617
- totalNew: 1840,
618
- totalPrev: 11007,
619
- active: 6920,
620
- activePrev: 5840,
621
- purchasing: 4290,
622
- purchasingPrev: 3520,
623
- repeat: 1802,
624
- repeatPrev: 1408,
625
- redeemers: 612,
626
- redeemersPrev: 504,
627
- memberSales: 311600,
628
- memberSalesPrev: 256800,
629
- series: [35e3, 38e3, 36e3, 42e3, 45e3, 43e3, 48e3, 52e3, 49e3, 55e3, 58e3, 56e3],
630
- prev: [28e3, 3e4, 29e3, 34e3, 36e3, 34e3, 38e3, 42e3, 4e4, 44e3, 47e3, 46e3]
631
- }
632
- }, O = (e, s) => s ? Math.round(e / s * 1e3) / 10 : 0, se = (e, s) => s ? Math.round((e - s) / s * 1e3) / 10 : 0, K = (e) => e >= 1e3 ? (e / 1e3).toFixed(1).replace(/\.0$/, "") + "k" : String(e), ce = (e) => "$" + e.toLocaleString("en-US", {
633
- minimumFractionDigits: 2,
634
- maximumFractionDigits: 2
635
- }), ds = ["neutral-black", "primary", "#FF9C52", "#FFB577", "#FFD0A3"], x = { text: "text-fm-color-typo-success", bg: "bg-fm-color-system-success-100" }, me = { text: "text-fm-color-typo-error", bg: "bg-fm-color-system-error-100" }, G = { text: "text-fm-color-typo-warning", bg: "bg-fm-color-system-warning-100" };
636
- function Y(e) {
637
- return e ? x.text : me.text;
638
- }
639
- function _e(e) {
640
- const s = [
641
- {
642
- id: "total",
643
- label: "Total members",
644
- count: e.totalMembers,
645
- desc: "All members through period end",
646
- denom: null
647
- },
648
- {
649
- id: "active",
650
- label: "Active members",
651
- count: e.activeMembers,
652
- desc: "Purchase or reward activity",
653
- denom: e.totalMembers
654
- },
655
- {
656
- id: "purchasing",
657
- label: "Purchase members",
658
- count: e.purchasingMembers,
659
- desc: "At least 1 completed order",
660
- denom: e.activeMembers
661
- },
662
- {
663
- id: "repeat",
664
- label: "Repeat members",
665
- count: e.repeatMembers,
666
- desc: "Had prior order history",
667
- denom: e.purchasingMembers
668
- },
669
- {
670
- id: "redeemers",
671
- label: "Reward redeemers",
672
- count: e.rewardRedeemers,
673
- desc: "Repeat member used reward",
674
- denom: e.repeatMembers
675
- }
676
- ], o = s.map(
677
- (i, c) => c === 0 || !i.denom ? null : O(i.count, i.denom)
678
- );
679
- let d = 1, a = 100;
680
- return o.forEach((i, c) => {
681
- c > 0 && i !== null && i < a && (a = i, d = c);
682
- }), {
683
- funnelStages: s.map((i, c) => {
684
- const p = o[c], m = c === d;
685
- return {
686
- id: i.id,
687
- label: i.label,
688
- desc: i.desc,
689
- countStr: K(i.count),
690
- conversionStr: p != null ? `${p}% convert` : "",
691
- convRateLabel: p != null ? `${p}% of prev` : "",
692
- hasConvRate: c > 0,
693
- hasDropLine: c > 0,
694
- isBiggestDrop: m,
695
- barColor: ds[c] ?? "#FFD0A3",
696
- barWidthPct: e.totalMembers > 0 && i.count > 0 ? Math.max(O(i.count, e.totalMembers), 1) : 0,
697
- dropClass: m ? `${G.text} bg-[#FFF0E2]` : "text-fm-color-typo-secondary bg-fm-color-neutral-gray-100"
698
- };
699
- }),
700
- funnelFootnote: `${e.totalMembers.toLocaleString()} total members`,
701
- convRates: o,
702
- biggestDropIdx: d
703
- };
704
- }
705
- function us(e) {
706
- const { funnelStages: s, funnelFootnote: o } = _e(e);
707
- return { funnelStages: s, funnelFootnote: o };
708
- }
709
- function ps(e) {
710
- return {
711
- categories: e.categories,
712
- series: e.series,
713
- prevSeries: e.prevSeries,
714
- totalFmt: ce(e.memberSales),
715
- deltaStr: `${e.deltaPct >= 0 ? "+" : ""}${e.deltaPct}%`,
716
- deltaClass: Y(e.deltaPct >= 0)
717
- };
718
- }
719
- function we(e) {
720
- const s = [
721
- {
722
- id: "new",
723
- label: "Newly joined",
724
- count: e.newlyJoined,
725
- labelClass: x.text,
726
- barColor: "typo-success"
727
- },
728
- {
729
- id: "frequent",
730
- label: "Frequent",
731
- count: e.frequent,
732
- labelClass: "text-fm-color-primary",
733
- barColor: "primary"
734
- },
735
- {
736
- id: "infrequent",
737
- label: "Infrequent",
738
- count: e.infrequent,
739
- labelClass: "text-fm-color-typo-secondary",
740
- barColor: "neutral-gray-300"
741
- },
742
- {
743
- id: "at-risk",
744
- label: "At-risk",
745
- count: e.atRisk,
746
- labelClass: G.text,
747
- barColor: "#FFC746"
748
- },
749
- {
750
- id: "dormant",
751
- label: "Dormant",
752
- count: e.dormant,
753
- labelClass: me.text,
754
- barColor: "#FFCFCC"
755
- }
756
- ], o = Math.max(...s.map((a) => a.count), 1);
757
- return {
758
- lifecycleSegs: s.map((a) => ({
759
- ...a,
760
- barPct: Math.round(a.count / o * 100),
761
- pctStr: `${e.totalMembers ? Math.round(a.count / e.totalMembers * 100) : 0}%`,
762
- countStr: K(a.count)
763
- })),
764
- lifecycleSummary: `${K(
765
- e.atRisk + e.dormant
766
- )} members are at-risk or dormant`
767
- };
768
- }
769
- function fs(e) {
770
- return we(e);
771
- }
772
- function ys(e) {
773
- const s = ae[e] ?? ae["this-month"], o = se(s.memberSales, s.memberSalesPrev), { funnelStages: d, funnelFootnote: a, convRates: l, biggestDropIdx: i } = _e({
774
- totalMembers: s.total,
775
- activeMembers: s.active,
776
- purchasingMembers: s.purchasing,
777
- repeatMembers: s.repeat,
778
- rewardRedeemers: s.redeemers
779
- }), c = {
780
- 1: { title: "Many members are not active", body: `Only ${l[1]}% of members took any action this period. Most who joined have gone quiet — an onboarding or reactivation campaign could help.`, primaryLabel: "Create reactivation campaign", secondaryLabel: "View inactive members", secondaryStage: "total" },
781
- 2: { title: "Active members aren't purchasing", body: `${l[2]}% of active members completed an order. Members are engaging but not buying — review purchase incentives or point triggers.`, primaryLabel: "Review purchase incentives", secondaryLabel: "View non-purchasing members", secondaryStage: "active" },
782
- 3: { title: "Members aren't returning after first purchase", body: `${l[3]}% of purchasing members have repeat history. Most buy once and don't come back — a retention campaign could help.`, primaryLabel: "Create retention campaign", secondaryLabel: "View one-time buyers", secondaryStage: "purchasing" },
783
- 4: { title: "Repeat members aren't using rewards", body: `Only ${l[4]}% of repeat members redeemed a reward. Your most loyal members may not be aware of rewards or find redemption unclear.`, primaryLabel: "Review reward setup", secondaryLabel: "View non-redeeming members", secondaryStage: "repeat" }
784
- }, m = {
785
- ...c[i] ?? c[4],
786
- signals: [
787
- { text: `${s.active.toLocaleString()} active members — ${l[1]}% of total base this period` },
788
- { text: `Member sales ${o >= 0 ? "+" : ""}${o}% — loyalty is ${o >= 0 ? "driving revenue growth" : "lagging behind revenue"}` },
789
- { text: `${s.totalNew} new members joined, ${K(s.total - s.totalNew)} are existing base` }
790
- ]
791
- }, S = O(s.repeat, s.purchasing), R = O(s.repeatPrev, s.purchasingPrev), P = O(s.redeemers, s.repeat), U = O(s.redeemersPrev, s.repeatPrev), L = (Z, D, T, ee, oe, ne = !1) => ({
792
- label: Z,
793
- value: D,
794
- sub: ee,
795
- delta: `${T >= 0 ? "+" : ""}${T}${ne ? "pp" : "%"}`,
796
- deltaClass: Y(T >= 0),
797
- trendUp: T >= 0,
798
- icon: oe
799
- }), E = [
800
- L("Total members", K(s.total), se(s.total, s.totalPrev), `+${s.totalNew} new`, "group"),
801
- L("Active members", K(s.active), se(s.active, s.activePrev), null, "bolt"),
802
- L("Member sales", ce(s.memberSales), o, null, "payments"),
803
- L("Repeat rate", `${S}%`, Math.round((S - R) * 10) / 10, null, "repeat", !0),
804
- L("Voucher redemption rate", `${P}%`, Math.round((P - U) * 10) / 10, null, "redeem", !0)
805
- ], { lifecycleSegs: V, lifecycleSummary: W } = we({
806
- totalMembers: s.total,
807
- newlyJoined: s.totalNew,
808
- frequent: Math.round(s.active * 0.4),
809
- infrequent: Math.round(s.active * 0.6),
810
- atRisk: Math.round((s.total - s.active) * 0.4),
811
- dormant: Math.round((s.total - s.active) * 0.6)
812
- }), J = se(s.redeemers, s.redeemersPrev), q = Math.round((P - U) * 10) / 10, h = [
813
- { label: "Total redeemed", value: String(s.redeemers), delta: `${J >= 0 ? "+" : ""}${J}%`, deltaClass: Y(J >= 0) },
814
- { label: "Redemption rate", value: `${P}%`, delta: `${q >= 0 ? "+" : ""}${q}pp`, deltaClass: Y(q >= 0) },
815
- { label: "Avg per member", value: `${(Math.round(s.redeemers / Math.max(s.repeat, 1) * 100) / 100).toFixed(2)}x`, delta: "+0.1x", deltaClass: x.text }
816
- ], Q = [
817
- { name: "Flat white freebie", count: "142", barPct: 100 },
818
- { name: "$5 cashback voucher", count: "98", barPct: 69 },
819
- { name: "50 bonus points", count: "73", barPct: 51 },
820
- { name: "Free size upgrade", count: "40", barPct: 28 }
821
- ], X = {
822
- categories: s.series.map((Z, D) => String(D + 1)),
823
- series: s.series,
824
- prevSeries: s.prev,
825
- totalFmt: ce(s.memberSales),
826
- deltaStr: `${o >= 0 ? "+" : ""}${o}%`,
827
- deltaClass: Y(o >= 0)
828
- }, re = [
829
- { name: "Re-engage at-risk members", date: "14 Jun", type: "SMS", metric: "18.4%", metricLabel: "click rate", statusClass: x.text },
830
- { name: "Reward push — loyal members", date: "10 Jun", type: "Email", metric: "32.1%", metricLabel: "redemption", statusClass: x.text },
831
- { name: "First purchase reward", date: "5 Jun", type: "Push", metric: "8.9%", metricLabel: "conversion", statusClass: G.text },
832
- { name: "Dormant win-back offer", date: "1 Jun", type: "Email", metric: "4.2%", metricLabel: "open rate", statusClass: me.text }
833
- ];
834
- return {
835
- kpis: E,
836
- funnelStages: d,
837
- funnelFootnote: a,
838
- insight: m,
839
- lifecycleSegs: V,
840
- lifecycleSummary: W,
841
- rewardKpis: h,
842
- topRewards: Q,
843
- sales: X,
844
- campaigns: re
845
- };
846
- }
847
- const bs = {
848
- total: "All members",
849
- active: "Active members",
850
- purchasing: "Purchase members",
851
- repeat: "Repeat members",
852
- redeemers: "Reward redeemers",
853
- new: "Newly joined members",
854
- frequent: "Frequent members",
855
- infrequent: "Infrequent members",
856
- "at-risk": "At-risk members",
857
- dormant: "Dormant members"
858
- }, he = [
859
- { initials: "AT", name: "Aisha Tan", email: "aisha.tan@gmail.com", joined: "12 Jan 2024", lastActive: "15 Jun 2026", points: "1,840", status: "Regular", statusClass: `${x.text} ${x.bg}` },
860
- { initials: "WL", name: "Wei Ming Lim", email: "weiming.lim@yahoo.com", joined: "3 Mar 2024", lastActive: "14 Jun 2026", points: "920", status: "Frequent", statusClass: "text-fm-color-primary bg-[#FFF0E2]" },
861
- { initials: "FB", name: "Farah Binte Ali", email: "farah.ali@hotmail.com", joined: "21 Apr 2023", lastActive: "13 Jun 2026", points: "3,210", status: "Regular", statusClass: `${x.text} ${x.bg}` },
862
- { initials: "RK", name: "Raj Kumar", email: "raj.kumar@gmail.com", joined: "8 Nov 2023", lastActive: "12 Jun 2026", points: "560", status: "Active", statusClass: `${x.text} ${x.bg}` },
863
- { initials: "JW", name: "Jennifer Wong", email: "jwong@outlook.com", joined: "15 Jul 2024", lastActive: "10 Jun 2026", points: "240", status: "Active", statusClass: `${x.text} ${x.bg}` },
864
- { initials: "AF", name: "Ahmad Faris", email: "ahmad.faris@gmail.com", joined: "2 Feb 2023", lastActive: "8 Jun 2026", points: "4,102", status: "Frequent", statusClass: "text-fm-color-primary bg-[#FFF0E2]" },
865
- { initials: "LC", name: "Lily Chen", email: "lily.chen@gmail.com", joined: "19 May 2024", lastActive: "5 Jun 2026", points: "120", status: "New", statusClass: `${x.text} ${x.bg}` },
866
- { initials: "MN", name: "Marcus Ng", email: "marcus.ng@yahoo.com", joined: "7 Aug 2023", lastActive: "1 Jun 2026", points: "780", status: "At-risk", statusClass: `${G.text} ${G.bg}` }
867
- ];
868
- function vs(e, s) {
869
- const o = ae[s] ?? ae["this-month"], a = {
870
- total: o.total,
871
- active: o.active,
872
- purchasing: o.purchasing,
873
- repeat: o.repeat,
874
- redeemers: o.redeemers
875
- }[e] ?? 0;
876
- return {
877
- title: bs[e] ?? "Members",
878
- countLabel: `Showing ${he.length} of ${a.toLocaleString()} members`,
879
- members: he
880
- };
881
- }
882
- const gs = { class: "px-24 flex flex-col gap-16 pb-24" }, xs = { class: "flex items-center justify-between gap-8 flex-wrap" }, hs = { class: "flex items-center gap-8 flex-wrap" }, _s = { class: "fm-typo-en-body-xs-400 text-fm-color-typo-tertiary -mt-8 text-right" }, ws = { class: "grid grid-cols-[3fr_2fr] xs:grid-cols-1 gap-16 items-start" }, $s = { class: "grid grid-cols-2 xs:grid-cols-1 gap-16" }, As = /* @__PURE__ */ M({
883
- __name: "LoyaltyDashboard",
884
- setup(e) {
885
- const s = Ee(), o = Ve(), { global: d, minor: a } = Ne, l = _(
886
- () => ve[o.currentCountry.value || "MY"] ?? ve.MY
887
- ), [i, c] = ls(
888
- "this-month",
889
- l.value
890
- ), p = $({
891
- startDate: i,
892
- endDate: c
893
- }), m = $(J(xe([i, c]))), S = $({ location: [] }), R = $(null), P = $(s.locations.length > 0), U = $(!1), L = $({}), E = $(null), V = $(null), W = $(null);
894
- function J([r, f]) {
895
- return { startDate: r, endDate: f };
896
- }
897
- fe(p, (r) => {
898
- R.value = null, r.startDate && r.endDate && (m.value = J(
899
- xe([r.startDate, r.endDate])
900
- ));
901
- });
902
- const q = _(() => ms(p.value)), h = _(() => ys(q.value)), Q = _(
903
- () => E.value ? us(E.value) : {
904
- funnelStages: h.value.funnelStages,
905
- funnelFootnote: h.value.funnelFootnote
906
- }
907
- ), X = _(
908
- () => V.value ? fs(V.value) : {
909
- lifecycleSegs: h.value.lifecycleSegs,
910
- lifecycleSummary: h.value.lifecycleSummary
911
- }
912
- ), re = _(
913
- () => W.value ? ps(W.value) : h.value.sales
914
- ), Z = _(() => {
915
- const r = L.value;
916
- return h.value.kpis.map((f) => f.label === "Total members" && r.totalMembers != null ? { ...f, value: de(r.totalMembers) } : f.label === "Active members" && r.activeMembers != null ? { ...f, value: de(r.activeMembers) } : f.label === "Member sales" && r.memberSales != null ? { ...f, value: $e(r.memberSales) } : f.label === "Repeat rate" && r.repeatRatePct != null ? { ...f, value: ue(r.repeatRatePct) } : f.label === "Voucher redemption rate" && r.voucherRedemptionRatePct != null ? { ...f, value: ue(r.voucherRedemptionRatePct) } : f);
917
- }), D = _(
918
- () => R.value ? vs(R.value, q.value) : null
919
- ), T = _(
920
- () => s.locations.map((r) => r._id.toString())
921
- );
922
- function ee(r) {
923
- return !r.startDate || !r.endDate ? "" : `${ye(r.startDate).format("D MMM")} – ${ye(r.endDate).format("D MMM YYYY")}`;
924
- }
925
- const oe = _(() => ee(p.value)), ne = _(() => ee(m.value));
926
- function de(r) {
927
- return r >= 1e3 ? `${(r / 1e3).toFixed(1).replace(/\.0$/, "")}k` : String(r);
928
- }
929
- function $e(r) {
930
- return "$" + r.toLocaleString("en-US", {
931
- minimumFractionDigits: 2,
932
- maximumFractionDigits: 2
933
- });
934
- }
935
- function ue(r) {
936
- return `${Math.round(r * 10) / 10}%`;
937
- }
938
- function Se(r) {
939
- if (r && typeof r == "object" && "value" in r && Number.isFinite(Number(r.value)))
940
- return Number(r.value);
941
- }
942
- function te() {
943
- return !p.value.startDate || !p.value.endDate || !P.value || T.value.length === 0 ? null : cs({
944
- dateRange: [p.value.startDate, p.value.endDate],
945
- selectedLocationIds: S.value.location,
946
- locationIds: T.value,
947
- timezone: l.value
948
- });
949
- }
950
- async function Fe() {
951
- const r = te();
952
- if (!r) return;
953
- const [
954
- f,
955
- ie,
956
- j,
957
- Le,
958
- De
959
- ] = await Promise.all([
960
- I.readTotalMembers(r),
961
- I.readActiveMembers(r),
962
- I.readMemberSales(r),
963
- I.readRepeatRate(r),
964
- I.readVoucherRedemptionRate(r)
965
- ]);
966
- L.value = {
967
- totalMembers: Se(f),
968
- activeMembers: ie.activeMembers,
969
- memberSales: j.memberSales,
970
- repeatRatePct: Le.repeatRatePct,
971
- voucherRedemptionRatePct: De.voucherRedemptionRatePct
972
- };
973
- }
974
- async function Me() {
975
- const r = te();
976
- r && (E.value = await I.readMemberFunnel(
977
- r
978
- ));
979
- }
980
- async function Ce() {
981
- const r = te();
982
- r && (V.value = await I.readMemberLifecycle(
983
- r
984
- ));
985
- }
986
- async function Re() {
987
- const r = te();
988
- r && (W.value = await I.readMemberSalesTrend(r));
989
- }
990
- async function pe() {
991
- await Promise.all([
992
- Fe(),
993
- Me(),
994
- Ce(),
995
- Re()
996
- ]);
997
- }
998
- fe([p, () => S.value.location], async () => {
999
- U.value && await a(pe);
1000
- }), Ie(async () => {
1001
- await d(async () => {
1002
- P.value || await s.readLocations(), P.value = !0, await pe(), U.value = !0;
1003
- });
1004
- });
1005
- function le(r) {
1006
- R.value = r;
1007
- }
1008
- function Pe() {
1009
- R.value = null;
1010
- }
1011
- return (r, f) => {
1012
- const ie = g("FmPageHead");
1013
- return u(), v(F, null, [
1014
- b(ie, { title: "Analytic" }),
1015
- t("div", gs, [
1016
- t("div", xs, [
1017
- b(Be, {
1018
- label: "Location: ",
1019
- "right-icon": "keyboard_arrow_down",
1020
- modelValue: y(S).location,
1021
- "onUpdate:modelValue": f[0] || (f[0] = (j) => y(S).location = j),
1022
- icon: "storefront",
1023
- "show-item-label": !0,
1024
- items: y(s).locationOptions
1025
- }, null, 8, ["modelValue", "items"]),
1026
- t("div", hs, [
1027
- b(be, {
1028
- "model-value": y(p),
1029
- "onUpdate:modelValue": f[1] || (f[1] = (j) => p.value = j)
1030
- }, null, 8, ["model-value"]),
1031
- f[3] || (f[3] = t("span", { class: "fm-typo-en-body-xs-600 text-fm-color-typo-tertiary" }, "vs", -1)),
1032
- b(be, {
1033
- "model-value": y(m),
1034
- "onUpdate:modelValue": f[2] || (f[2] = (j) => m.value = j)
1035
- }, null, 8, ["model-value"])
1036
- ])
1037
- ]),
1038
- t("div", _s, n(y(oe)) + " · vs. " + n(y(ne)), 1),
1039
- b(dt, { kpis: y(Z) }, null, 8, ["kpis"]),
1040
- t("section", ws, [
1041
- b(Ht, {
1042
- stages: y(Q).funnelStages,
1043
- footnote: y(Q).funnelFootnote,
1044
- interactive: !y(E),
1045
- onSelect: le
1046
- }, null, 8, ["stages", "footnote", "interactive"]),
1047
- b(at, {
1048
- insight: y(h).insight,
1049
- onViewSegment: le
1050
- }, null, 8, ["insight"])
1051
- ]),
1052
- t("section", $s, [
1053
- b(gt, {
1054
- segments: y(X).lifecycleSegs,
1055
- summary: y(X).lifecycleSummary,
1056
- interactive: !y(V),
1057
- onSelect: le
1058
- }, null, 8, ["segments", "summary", "interactive"]),
1059
- b(as, {
1060
- kpis: y(h).rewardKpis,
1061
- "top-rewards": y(h).topRewards
1062
- }, null, 8, ["kpis", "top-rewards"]),
1063
- b(ns, { sales: y(re) }, null, 8, ["sales"]),
1064
- b(Ye, {
1065
- campaigns: y(h).campaigns
1066
- }, null, 8, ["campaigns"])
1067
- ]),
1068
- y(D) ? (u(), C(kt, {
1069
- key: 0,
1070
- title: y(D).title,
1071
- "count-label": y(D).countLabel,
1072
- members: y(D).members,
1073
- onClose: Pe
1074
- }, null, 8, ["title", "count-label", "members"])) : H("", !0)
1075
- ])
1076
- ], 64);
1077
- };
1078
- }
1079
- });
1080
- export {
1081
- As as default
1082
- };