@feedmepos/mf-connect 0.1.98-beta.4 → 0.1.98-beta.40

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 (135) hide show
  1. package/package.json +3 -3
  2. package/dist/App-BGv_JihW.js +0 -4642
  3. package/dist/AuditLogs-53rnjCmD.js +0 -2013
  4. package/dist/Bin-B0Q_WpuY.js +0 -166
  5. package/dist/Broadcast-PiJE1Syl.js +0 -266
  6. package/dist/CampaignTypeDialog.vue_vue_type_script_setup_true_lang-Dz2KsBZl.js +0 -120
  7. package/dist/Card-DlqMu3Sl.js +0 -910
  8. package/dist/ChipMenu.vue_vue_type_script_setup_true_lang-BPpZZNkr.js +0 -97
  9. package/dist/Collections-BWcBlDDR.js +0 -540
  10. package/dist/Credit-CMfcuj5n.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-DCvH0lGA.js +0 -3377
  14. package/dist/EffectSheet.vue_vue_type_style_index_0_lang-B_FyeWq4.js +0 -351
  15. package/dist/Experience-DacFmPYy.js +0 -259
  16. package/dist/FileSaver.min-CMZKiGkX.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-DXKnGuhR.js +0 -330
  20. package/dist/Grab-DaBL5y3H.js +0 -300
  21. package/dist/ImageInput.vue_vue_type_script_setup_true_lang-BaY_nQio.js +0 -145
  22. package/dist/Index-2UrjF35w.js +0 -259
  23. package/dist/JsonViewer-v8_C7l5N.js +0 -87
  24. package/dist/LocationPicker.vue_vue_type_script_setup_true_lang-BuVBZsCN.js +0 -48
  25. package/dist/LottieAnimation.vue_vue_type_script_setup_true_lang-6wUxYqWe.js +0 -18737
  26. package/dist/LoyaltyDashboard-DIArPp71.js +0 -1082
  27. package/dist/MainLayout-BjvTxTPG.js +0 -41
  28. package/dist/Marketing-DEp--hFd.js +0 -235
  29. package/dist/MarketingMaterialEdit-BHL8Ptsu.js +0 -1035
  30. package/dist/Member-BU1YxoEV.js +0 -280
  31. package/dist/MemberList-zjvJBYf0.js +0 -1601
  32. package/dist/MemberOverview-wL_ailso.js +0 -58
  33. package/dist/MemberTransactions-BvyLV69V.js +0 -45
  34. package/dist/MessageInput.vue_vue_type_script_setup_true_lang-_YOM1SlS.js +0 -60802
  35. package/dist/Mission-BNYk_aPM.js +0 -334
  36. package/dist/Overview-BToUlbsH.js +0 -1079
  37. package/dist/Point-B-L2326L.js +0 -262
  38. package/dist/Promotion-sMKGkL4w.js +0 -397
  39. package/dist/Queue-D3vUkKN9.js +0 -260
  40. package/dist/Record-BViEbZZ8.js +0 -342
  41. package/dist/Review-CMBAk-Qu.js +0 -1067
  42. package/dist/ReviewFacebookCallback-BCJTFy3b.js +0 -243
  43. package/dist/ReviewGoogleCallback-DKUG075r.js +0 -208
  44. package/dist/Reward-CZsvsYW_.js +0 -442
  45. package/dist/RewardGroup.vue_vue_type_script_setup_true_lang-BASPLoRa.js +0 -149
  46. package/dist/RuleGroup.vue_vue_type_script_setup_true_lang-B90meAnV.js +0 -1911
  47. package/dist/Segment-CN8NEg8w.js +0 -1014
  48. package/dist/SelectItems.vue_vue_type_script_setup_true_lang-BGyYQST4.js +0 -181
  49. package/dist/SendCollectionSheet.vue_vue_type_script_setup_true_lang-BxW-Kc6t.js +0 -188
  50. package/dist/SendVoucherSheet.vue_vue_type_script_setup_true_lang-C9dwcEr5.js +0 -226
  51. package/dist/Setting-DLPbZW11.js +0 -2047
  52. package/dist/SettingAdvertisements-BIq8gS5g.js +0 -264
  53. package/dist/Store-DNB3gUIR.js +0 -1996
  54. package/dist/StoreRewards-BoEjVFk6.js +0 -540
  55. package/dist/StoreTransactions.vue_vue_type_script_setup_true_lang-Bv9ABH1K.js +0 -1130
  56. package/dist/TargetLocationInput.vue_vue_type_script_setup_true_lang-DNHgSfIg.js +0 -7007
  57. package/dist/Tier-kQ6X9h-t.js +0 -969
  58. package/dist/Title-XosVAF2Y.js +0 -566
  59. package/dist/Transaction-CO6oLPKR.js +0 -39
  60. package/dist/UserRuleGroup.vue_vue_type_script_setup_true_lang-McMR2Y7Y.js +0 -130
  61. package/dist/Voucher-CogaCxx2.js +0 -421
  62. package/dist/VoucherEditor.vue_vue_type_script_setup_true_lang-DjHwHFBq.js +0 -421
  63. package/dist/ZodTextField.vue_vue_type_script_setup_true_lang-Dor9klZ8.js +0 -56
  64. package/dist/_plugin-vue_export-helper-CHgC5LLL.js +0 -9
  65. package/dist/app-C9UPXbDs.js +0 -59737
  66. package/dist/app-wDQR6dpb.js +0 -39
  67. package/dist/app.js +0 -7
  68. package/dist/business-CYpW6-Ls.js +0 -82
  69. package/dist/campaign-BtLa7KeF.js +0 -277
  70. package/dist/campaign-CpzFl8Vr.js +0 -4
  71. package/dist/campaign-CtoHyXVT.js +0 -219
  72. package/dist/campaign.enum-DiLhS5il.js +0 -26
  73. package/dist/campaign.fn-CLB9gBHo.js +0 -776
  74. package/dist/collectible.enum-CmwWcfBh.js +0 -19
  75. package/dist/duplicateCampaign-DGhOBd5l.js +0 -51
  76. package/dist/effect-CY5Y6sxy.js +0 -89
  77. package/dist/email-addresses-BEnOHWj_.js +0 -705
  78. package/dist/export-BgYu7SCd.js +0 -97
  79. package/dist/feedme.ico +0 -0
  80. package/dist/helper-DTDgoyEh.js +0 -86
  81. package/dist/index-B4IAURA_.js +0 -3684
  82. package/dist/index-BZVhi3ZG.js +0 -28900
  83. package/dist/index-C8ehLvXG.js +0 -31
  84. package/dist/index-CDk0Itu1.js +0 -32
  85. package/dist/index-CLZnH782.js +0 -54
  86. package/dist/index-CNKP1_IW.js +0 -30289
  87. package/dist/index-CP-7qATP.js +0 -67
  88. package/dist/index-Ck3PeOQ5.js +0 -1157
  89. package/dist/index-CuvNtC_M.js +0 -61
  90. package/dist/index-DntoqWT_.js +0 -85
  91. package/dist/index-DtyWsBgg.js +0 -3
  92. package/dist/index-P8Qh2yIP.js +0 -52
  93. package/dist/index-goRooQn5.js +0 -2802
  94. package/dist/index-kS20rM5m.js +0 -8910
  95. package/dist/index-vh86FiIQ.js +0 -10
  96. package/dist/index-zF0gIg9H.js +0 -151
  97. package/dist/index.esm2017-C79fd6cz.js +0 -3069
  98. package/dist/loading-Dfr_97Ri.js +0 -72
  99. package/dist/lodash-BX2mJOxF.js +0 -3678
  100. package/dist/membership-ncNOUJwj.js +0 -255
  101. package/dist/moment-D7p2TMB-.js +0 -2583
  102. package/dist/money-vEtTzt04.js +0 -15
  103. package/dist/number-BjgTlMsp.js +0 -12
  104. package/dist/object-BYyQ0PZp.js +0 -18
  105. package/dist/objectId-dwaoBfJX.js +0 -7
  106. package/dist/objectid-C9DiLwge.js +0 -145
  107. package/dist/plugins-BVGqEmE1.js +0 -1788
  108. package/dist/reward-BGTLhhzT.js +0 -36
  109. package/dist/rule-BB2VX3Gy.js +0 -1969
  110. package/dist/string-Gcy5IM_H.js +0 -32
  111. package/dist/style.css +0 -4
  112. package/dist/template-DLiHnpjK.js +0 -188
  113. package/dist/tier-BJDhpH6X.js +0 -4
  114. package/dist/tiers/tier=1.png +0 -0
  115. package/dist/tiers/tier=2.png +0 -0
  116. package/dist/tiers/tier=3.png +0 -0
  117. package/dist/tiers/tier=4.png +0 -0
  118. package/dist/titles/type=diamond.png +0 -0
  119. package/dist/titles/type=drinks.png +0 -0
  120. package/dist/titles/type=food.png +0 -0
  121. package/dist/titles/type=heart.png +0 -0
  122. package/dist/titles/type=medal.png +0 -0
  123. package/dist/titles/type=spend.png +0 -0
  124. package/dist/titles/type=thank-you.png +0 -0
  125. package/dist/titles/type=thumbs-up.png +0 -0
  126. package/dist/titles/type=years.png +0 -0
  127. package/dist/trigger-DrKnfSoi.js +0 -213
  128. package/dist/tslib.es6-BCqqvLQ_.js +0 -44
  129. package/dist/type_drinks-D6jnpvwV.js +0 -4
  130. package/dist/useMarketingMaterialActions-B6zaDbNE.js +0 -107
  131. package/dist/usePagination-Dgd-onzU.js +0 -220
  132. package/dist/user-BcDUmNrZ.js +0 -55
  133. package/dist/vue-i18n-DNBGmwox.js +0 -2365
  134. package/dist/xlsx-BX-U0_Au.js +0 -15491
  135. package/dist/zod-Mx51GBPY.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-Dfr_97Ri.js";
6
- import { g as k, S as Te, b as je } from "./index-BZVhi3ZG.js";
7
- import { u as Ee } from "./business-CYpW6-Ls.js";
8
- import { m as z, C as ve } from "./index-Ck3PeOQ5.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
- };