@smartico/public-api 0.0.379 → 0.0.380
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nvmrc +1 -1
- package/README.md +31 -2
- package/dist/Store/BuyStoreItemErrorCode.d.ts +9 -1
- package/dist/Tournaments/TournamentRegistrationError.d.ts +3 -1
- package/dist/WSAPI/WSAPI.d.ts +13 -1235
- package/dist/WSAPI/WSAPIAvatars.d.ts +78 -0
- package/dist/WSAPI/WSAPIBase.d.ts +71 -0
- package/dist/WSAPI/WSAPIBonuses.d.ts +26 -0
- package/dist/WSAPI/WSAPIClans.d.ts +388 -0
- package/dist/WSAPI/WSAPIGamePick.d.ts +308 -0
- package/dist/WSAPI/WSAPIGeneral.d.ts +30 -0
- package/dist/WSAPI/WSAPIInbox.d.ts +126 -0
- package/dist/WSAPI/WSAPIJackpots.d.ts +109 -0
- package/dist/WSAPI/WSAPILeaderBoard.d.ts +25 -0
- package/dist/WSAPI/WSAPIMiniGames.d.ts +94 -0
- package/dist/WSAPI/WSAPIMissions.d.ts +517 -0
- package/dist/WSAPI/WSAPIRaffles.d.ts +119 -0
- package/dist/WSAPI/WSAPIStore.d.ts +656 -0
- package/dist/WSAPI/WSAPITournaments.d.ts +501 -0
- package/dist/WSAPI/WSAPITypes.d.ts +145 -85
- package/dist/WSAPI/WSAPIUser.d.ts +383 -0
- package/dist/index.js +3818 -1528
- package/dist/index.js.map +1 -1
- package/dist/index.modern.mjs +3180 -981
- package/dist/index.modern.mjs.map +1 -1
- package/docs/api/README.md +56 -2
- package/docs/api/classes/WSAPIAvatars.md +116 -0
- package/docs/api/classes/WSAPIBonuses.md +50 -0
- package/docs/api/classes/WSAPIClans.md +66 -0
- package/docs/api/classes/WSAPIGamePick.md +181 -0
- package/docs/api/classes/WSAPIGeneral.md +63 -0
- package/docs/api/classes/WSAPIInbox.md +277 -0
- package/docs/api/classes/WSAPIJackpots.md +185 -0
- package/docs/api/classes/WSAPILeaderBoard.md +39 -0
- package/docs/api/classes/WSAPIMiniGames.md +175 -0
- package/docs/api/classes/WSAPIMissions.md +611 -0
- package/docs/api/classes/WSAPIRaffles.md +195 -0
- package/docs/api/classes/WSAPIStore.md +744 -0
- package/docs/api/classes/WSAPITournaments.md +104 -0
- package/docs/api/classes/WSAPIUser.md +200 -0
- package/docs/api/enumerations/AchievementClaimPeriodTypeId.md +13 -0
- package/docs/api/enumerations/BuyStoreItemErrorCode.md +32 -0
- package/docs/api/interfaces/ClanPrizePublicMeta.md +41 -0
- package/docs/api/interfaces/ClanPrizeStructureEntry.md +7 -1
- package/docs/api/interfaces/ClanPublicMeta.md +4 -0
- package/docs/api/interfaces/SAWTemplateUI.md +10 -0
- package/docs/api/interfaces/TAchCategory.md +19 -4
- package/docs/api/interfaces/TBuyStoreItemResult.md +26 -8
- package/docs/api/interfaces/TMissionClaimRewardResult.md +14 -3
- package/docs/api/interfaces/TMissionOptInResult.md +10 -3
- package/docs/api/interfaces/TMissionOrBadge.md +6 -2
- package/docs/api/interfaces/TStoreItem.md +21 -1
- package/docs/api/interfaces/TTournamentDetailed.md +8 -0
- package/docs/api/interfaces/UserAchievement.md +1 -1
- package/docs/ui/clans/UIGuide_getClanInfo.md +153 -0
- package/docs/ui/clans/UIGuide_getClans.md +172 -0
- package/docs/ui/clans/UIGuide_joinClan.md +196 -0
- package/docs/ui/missions/UIGuide_getAchCategories.md +17 -0
- package/docs/ui/missions/UIGuide_getBadges.md +237 -0
- package/docs/ui/missions/UIGuide_getMissions.md +268 -0
- package/docs/ui/missions/UIGuide_requestMissionClaimReward.md +21 -0
- package/docs/ui/missions/UIGuide_requestMissionOptIn.md +16 -0
- package/docs/ui/store/UIGuide_buyStoreItem.md +133 -0
- package/docs/ui/store/UIGuide_getStoreCategories.md +19 -0
- package/docs/ui/store/UIGuide_getStoreItems.md +264 -0
- package/docs/ui/store/UIGuide_getStorePurchasedItems.md +122 -0
- package/docs/ui/tournaments/UIGuide_getClanTournamentPlayers.md +140 -0
- package/docs/ui/tournaments/UIGuide_getTournamentInstanceInfo.md +265 -0
- package/docs/ui/tournaments/UIGuide_getTournamentsList.md +236 -0
- package/docs/ui/tournaments/UIGuide_registerInTournament.md +156 -0
- package/docs/ui/user/UIGuide_getLevels.md +162 -0
- package/docs/ui/user/UIGuide_getUserProfile.md +213 -0
- package/package.json +3 -3
- package/scripts/postdoc-cleanup.js +195 -0
- package/src/Missions/MissionsUtils.ts +5 -2
- package/src/Store/BuyStoreItemErrorCode.ts +8 -0
- package/src/Store/StoreItem.ts +1 -0
- package/src/Store/StoreItemPurchased.ts +1 -0
- package/src/Store/StoreItemType.ts +1 -1
- package/src/Tournaments/TournamentRegistrationError.ts +4 -0
- package/src/WSAPI/WSAPI.ts +27 -2007
- package/src/WSAPI/WSAPIAvatars.ts +127 -0
- package/src/WSAPI/WSAPIBase.ts +93 -0
- package/src/WSAPI/WSAPIBonuses.ts +54 -0
- package/src/WSAPI/WSAPIClans.ts +417 -0
- package/src/WSAPI/WSAPIGamePick.ts +382 -0
- package/src/WSAPI/WSAPIGeneral.ts +57 -0
- package/src/WSAPI/WSAPIInbox.ts +222 -0
- package/src/WSAPI/WSAPIJackpots.ts +212 -0
- package/src/WSAPI/WSAPILeaderBoard.ts +40 -0
- package/src/WSAPI/WSAPIMiniGames.ts +181 -0
- package/src/WSAPI/WSAPIMissions.ts +572 -0
- package/src/WSAPI/WSAPIRaffles.ts +175 -0
- package/src/WSAPI/WSAPIStore.ts +732 -0
- package/src/WSAPI/WSAPITournaments.ts +541 -0
- package/src/WSAPI/WSAPITypes.ts +145 -87
- package/src/WSAPI/WSAPIUser.ts +456 -0
- package/tsconfig.json +17 -1
- package/docs/api/classes/WSAPI.md +0 -1927
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# UI Guide — `getBadges`
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
- Badges are completion-tracked achievements that **auto-unlock** when their
|
|
5
|
+
criteria are met. No opt-in step, no claim step, no recurring cycles.
|
|
6
|
+
- The same `TMissionOrBadge` shape is reused; treat the mission-only fields
|
|
7
|
+
on a badge object as not-applicable (see method TSDoc for the list).
|
|
8
|
+
- The default Smartico UI renders badges as a **category-grouped grid** —
|
|
9
|
+
not a flat list and not as status tabs. Each category section shows
|
|
10
|
+
`completed / total` in its header.
|
|
11
|
+
- No `onUpdate` subscription. Poll on the 30 s cache TTL or re-fetch on a
|
|
12
|
+
domain event your app already handles (e.g. after a game round, after a
|
|
13
|
+
bonus claim).
|
|
14
|
+
- Loading indicator: skip on cache hit; show a brief shimmer on cold fetch
|
|
15
|
+
(typically 100–500 ms).
|
|
16
|
+
|
|
17
|
+
## List view organization
|
|
18
|
+
|
|
19
|
+
Group by category (`category_ids`) and resolve display metadata via
|
|
20
|
+
[`getAchCategories`](../../api/classes/WSAPIMissions.md#getachcategories).
|
|
21
|
+
The category list serves both missions and badges — use it directly.
|
|
22
|
+
|
|
23
|
+
Recommended layout — sectioned grid:
|
|
24
|
+
|
|
25
|
+
1. Fetch badges + categories in parallel.
|
|
26
|
+
2. Sort categories by `order` ascending.
|
|
27
|
+
3. For each category, filter `badges.filter(b => b.category_ids.includes(cat.id))`.
|
|
28
|
+
4. Render one collapsible section per non-empty category, with a header
|
|
29
|
+
showing `<name> (<completed>/<total>)`.
|
|
30
|
+
5. Items inside a section render as a fixed-cell grid (typically 3–4
|
|
31
|
+
columns on desktop, 2 on mobile — see "Mobile vs desktop").
|
|
32
|
+
|
|
33
|
+
Items with `category_ids: []` (uncategorized): the SDK does NOT return a
|
|
34
|
+
synthetic "Other" category. If your UI surfaces uncategorized badges,
|
|
35
|
+
generate that bucket client-side. Many badge UIs simply hide uncategorized
|
|
36
|
+
badges.
|
|
37
|
+
|
|
38
|
+
A badge can appear in multiple categories — duplicate rendering across
|
|
39
|
+
sections is intentional. Do NOT dedupe; users navigate categories
|
|
40
|
+
independently.
|
|
41
|
+
|
|
42
|
+
**Sort within a category**: typical priority — `is_completed === true`
|
|
43
|
+
first (most recently completed by `complete_date_ts` DESC), then
|
|
44
|
+
in-progress (by `position` ASC), then not-yet-started (by `position` ASC),
|
|
45
|
+
then expired (by `position` ASC). `position` is the operator-configured
|
|
46
|
+
display order; lower = higher priority.
|
|
47
|
+
|
|
48
|
+
## Badge card / tile
|
|
49
|
+
|
|
50
|
+
Fields to render on the card:
|
|
51
|
+
|
|
52
|
+
- `image` — documented source size 256×256 px. Render at a 1:1 aspect
|
|
53
|
+
ratio. There is no built-in placeholder for missing images — provide a
|
|
54
|
+
neutral fallback (e.g. a generic locked-badge silhouette).
|
|
55
|
+
- `name` — the title. Translated server-side.
|
|
56
|
+
- Stage counter — show **`<completed_tasks> / <total_tasks>`** computed
|
|
57
|
+
from `tasks` (count `t.is_completed === true` over `tasks.length`), NOT
|
|
58
|
+
a percentage `progress` bar. The default Smartico UI deliberately keeps
|
|
59
|
+
task descriptions hidden ("surprise mechanics") and shows only the count
|
|
60
|
+
of stages cleared. The exception: a single-task badge can show just a
|
|
61
|
+
completion check-mark instead of `1/1`.
|
|
62
|
+
- Availability chip — drive from `badgeTimeLimitState`. See "Status-specific
|
|
63
|
+
visual treatments" for the per-state visual treatment.
|
|
64
|
+
- `ribbon` — optional tag rendered as a corner badge (`sale`, `hot`, `new`,
|
|
65
|
+
`vip` or a custom 250×300 px image URL). Render top-right on the card.
|
|
66
|
+
|
|
67
|
+
Fields NOT on the card (detail-view only): `description`, full task list
|
|
68
|
+
with names, `hint_text` (T&C), category memberships.
|
|
69
|
+
|
|
70
|
+
**Click target**: whole card opens the detail view. There is no action
|
|
71
|
+
button on the card itself — badges have no opt-in or claim flow.
|
|
72
|
+
|
|
73
|
+
## Detail view / popup
|
|
74
|
+
|
|
75
|
+
Top-to-bottom layout:
|
|
76
|
+
|
|
77
|
+
1. **Hero image** — `image` rendered at a larger size (typically
|
|
78
|
+
320×320 px or full-width up to a max). Apply the same status overlay
|
|
79
|
+
as the card (see "Status-specific visual treatments").
|
|
80
|
+
2. **Title & availability chip** — `name` + status chip.
|
|
81
|
+
3. **Description** — `description` HTML.
|
|
82
|
+
4. **Task list** — render `tasks[].name` as a vertical list, each with
|
|
83
|
+
a check-mark when `task.is_completed === true`. Do NOT render
|
|
84
|
+
`tasks[].progress` percentages; the badge UX convention is "stages
|
|
85
|
+
cleared", not "percent complete". Optionally show stage `points_reward`
|
|
86
|
+
beside each task name if the brand surfaces task rewards.
|
|
87
|
+
5. **Reward summary** — `reward` HTML if non-empty.
|
|
88
|
+
6. **Timing block** — formatted `active_from_ts` / `active_till_ts` or
|
|
89
|
+
completion date `complete_date_ts`. See "Countdown / timing format".
|
|
90
|
+
7. **T&C** — `hint_text` if present, in a smaller collapsible
|
|
91
|
+
"Terms" panel.
|
|
92
|
+
8. **Related games** — `related_games` array rendered as a horizontal
|
|
93
|
+
game-tile strip if non-empty. Each tile clicks through to the game
|
|
94
|
+
using `related_games[i].game_public_meta.link`.
|
|
95
|
+
|
|
96
|
+
No primary CTA button at the bottom — badges are passive. If the brand
|
|
97
|
+
wants to drive engagement from the popup, surface a `related_games` strip
|
|
98
|
+
or a deep-link CTA derived from the brand's own configuration; the SDK
|
|
99
|
+
itself does not return one for badges.
|
|
100
|
+
|
|
101
|
+
## Action button decision matrix
|
|
102
|
+
|
|
103
|
+
Badges have **no action buttons**. They auto-unlock when criteria are met.
|
|
104
|
+
Anything that looks like an action button in a badge view is either:
|
|
105
|
+
|
|
106
|
+
- A close / back affordance (popup chrome), or
|
|
107
|
+
- A "View related game" link derived from `related_games[i].game_public_meta.link`, or
|
|
108
|
+
- A custom brand-defined CTA the operator wires up externally — the SDK
|
|
109
|
+
does not return a `cta_action` payload for badges in practice.
|
|
110
|
+
|
|
111
|
+
Do NOT render opt-in buttons, claim buttons, or anything that mutates
|
|
112
|
+
state. Both surfaces would no-op against badge IDs (no server endpoint
|
|
113
|
+
accepts a badge for opt-in / claim).
|
|
114
|
+
|
|
115
|
+
## Image / asset specs
|
|
116
|
+
|
|
117
|
+
| Field | Documented source size | Aspect ratio | Fallback |
|
|
118
|
+
|---|---|---|---|
|
|
119
|
+
| `image` | 256×256 px | 1:1 | Neutral locked-badge silhouette |
|
|
120
|
+
| `ribbon` (custom URL) | 250×300 px | 5:6 portrait | Hide ribbon if URL fails to load |
|
|
121
|
+
| `related_games[i].game_public_meta.image` | per game-catalog spec (1:1) | 1:1 | Hide tile if image missing |
|
|
122
|
+
|
|
123
|
+
If `image` is missing or fails to load: render the fallback at the same
|
|
124
|
+
size. Do not show a broken-image icon — this UI surface is consumer-facing
|
|
125
|
+
gamification and a broken image undermines the reward fantasy.
|
|
126
|
+
|
|
127
|
+
## Status-specific visual treatments
|
|
128
|
+
|
|
129
|
+
Drive from `badgeTimeLimitState` first, then layer completion state on
|
|
130
|
+
top.
|
|
131
|
+
|
|
132
|
+
| `badgeTimeLimitState` | Card visual | Availability chip text |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| `BeforeStartDate` (0) — window hasn't started | Grayscale + subtle dim overlay. Lock-icon corner badge if `is_locked === true`. | `"Starts on <date>"` from `active_from_ts` |
|
|
135
|
+
| `AfterStartDateNoProgress` (1) — active, no progress yet, no end date | Full-color, no overlay | No chip (or a neutral `"Available"`) |
|
|
136
|
+
| `AfterStartDateNoProgressAndEndDate` (2) — active, no progress, has end date | Full-color | `"Until <date>"` from `active_till_ts` |
|
|
137
|
+
| `AfterStartDateWithProgressAndEndDate` (3) — active with progress, has end date | Full-color | `"<countdown>"` to `active_till_ts` (see "Countdown / timing format") |
|
|
138
|
+
| `AfterEndDateNotStarted` (4) — expired, never started | Grayscale + dim overlay + slash/X overlay | `"Expired"` |
|
|
139
|
+
| `AfterEndDateWithProgress` (5) — expired with some progress | Grayscale + dim overlay | `"Ended"` (or `"Expired"`) |
|
|
140
|
+
|
|
141
|
+
On top of the time-window treatment, layer the completion state:
|
|
142
|
+
|
|
143
|
+
- `is_completed === true` — color-restored card (override grayscale),
|
|
144
|
+
prominent check-mark corner badge, glow / shine accent. The default
|
|
145
|
+
Smartico UI shows the most-recently-completed badges first within a
|
|
146
|
+
category by `complete_date_ts` DESC.
|
|
147
|
+
- `is_completed === false` and at least one `task.is_completed === true` —
|
|
148
|
+
show the stage counter prominently (e.g. larger font, contrasting color)
|
|
149
|
+
to signal in-progress.
|
|
150
|
+
- All other cases — neutral card, stage counter at default emphasis.
|
|
151
|
+
|
|
152
|
+
## Countdown / timing format
|
|
153
|
+
|
|
154
|
+
Time displayed varies by badge state. Recommended format ("compact"):
|
|
155
|
+
|
|
156
|
+
- **Time-until-start** (`badgeTimeLimitState === BeforeStartDate`):
|
|
157
|
+
- More than 7 days away → `"Starts <date>"` (e.g. `"Starts 12 Jun"`)
|
|
158
|
+
- 1–7 days → `"Starts in <N> days"`
|
|
159
|
+
- < 24 h → `"Starts in <Hh Mm>"`
|
|
160
|
+
- **Time-until-end** (`AfterStartDateWithProgressAndEndDate`,
|
|
161
|
+
`AfterStartDateNoProgressAndEndDate`):
|
|
162
|
+
- > 7 days → `"Until <date>"`
|
|
163
|
+
- 1–7 days → `"<N> days left"`
|
|
164
|
+
- 1–24 h → `"<Hh Mm> left"`
|
|
165
|
+
- < 1 h → `"<Mm Ss> left"`, updated every second
|
|
166
|
+
- **Completed**: `"Completed <relative>"` (e.g. `"Completed 2 days ago"`)
|
|
167
|
+
from `complete_date_ts`. Tooltip shows full timestamp.
|
|
168
|
+
- **Expired / ended**: just the static label `"Expired"` or `"Ended"`.
|
|
169
|
+
|
|
170
|
+
Update cadence:
|
|
171
|
+
- > 1 hour away from boundary → refresh once a minute is enough.
|
|
172
|
+
- < 1 hour → refresh once a second.
|
|
173
|
+
- Avoid rendering ticking timers for badges that don't have an `active_till_ts`
|
|
174
|
+
(most badges) — most badges are open-ended.
|
|
175
|
+
|
|
176
|
+
## Empty / loading / error states
|
|
177
|
+
|
|
178
|
+
- **Loading (cold fetch)**: render a shimmer grid (skeleton cards) matching
|
|
179
|
+
the eventual grid layout. Show category-section headers if cached, else
|
|
180
|
+
shimmer the entire view.
|
|
181
|
+
- **Loading (cache hit)**: do not render a loading state. The promise
|
|
182
|
+
resolves within a microtask.
|
|
183
|
+
- **Empty result**: `[]` from the server means the brand has no badges
|
|
184
|
+
configured for the user (rare). Render a neutral empty-state illustration
|
|
185
|
+
with copy like `"No badges yet"`. Do NOT show this state during the
|
|
186
|
+
cold-fetch shimmer.
|
|
187
|
+
- **Error**: cache the prior result if any and keep rendering it; show a
|
|
188
|
+
small non-blocking error banner. Retry on the next poll tick. Do not
|
|
189
|
+
break the page on a single fetch failure.
|
|
190
|
+
|
|
191
|
+
## Animations / transitions
|
|
192
|
+
|
|
193
|
+
- **List entry**: cards fade-in on first render. Within a category section,
|
|
194
|
+
the default Smartico UI staggers the fade by ~30 ms per card.
|
|
195
|
+
- **Category section expand/collapse**: smooth height animation (~200 ms
|
|
196
|
+
ease-out).
|
|
197
|
+
- **Badge completion celebration**: when `is_completed` flips from `false`
|
|
198
|
+
to `true` between fetches (your consumer detects this by diffing the
|
|
199
|
+
prior list), play a celebration animation on the affected card —
|
|
200
|
+
shine sweep + check-mark scale-in. The default Smartico UI also opens
|
|
201
|
+
the detail popup automatically on a freshly-completed badge if the
|
|
202
|
+
user is currently viewing the badge surface; mirror this only if your
|
|
203
|
+
product surface justifies it.
|
|
204
|
+
- **Stage tick**: when `completed_tasks` increases between fetches without
|
|
205
|
+
full completion, briefly highlight the stage counter (color flash for
|
|
206
|
+
~500 ms) to acknowledge progress.
|
|
207
|
+
|
|
208
|
+
## Mobile vs desktop
|
|
209
|
+
|
|
210
|
+
- **Grid columns**: desktop typically 3–4 columns, tablet 2–3, mobile 2.
|
|
211
|
+
Cards are square (1:1 image aspect ratio drives the layout).
|
|
212
|
+
- **Card density**: mobile cards may hide the ribbon to reduce visual
|
|
213
|
+
noise; the stage counter remains.
|
|
214
|
+
- **Detail view**: mobile opens as a full-screen modal (slide-up); desktop
|
|
215
|
+
opens as a centered popup with backdrop dim.
|
|
216
|
+
- **Category sections**: mobile may default-collapse all sections except
|
|
217
|
+
the first one with non-zero progress; desktop typically renders all
|
|
218
|
+
sections expanded.
|
|
219
|
+
|
|
220
|
+
## Performance
|
|
221
|
+
|
|
222
|
+
- Diff the badge list between fetches to detect completion / stage events
|
|
223
|
+
for animations. Avoid full re-renders on every poll tick — keyed
|
|
224
|
+
per-card rendering matters because the celebration animations break
|
|
225
|
+
if the DOM nodes are recreated.
|
|
226
|
+
- The `image` URLs are CDN-hosted; let the browser cache them. Lazy-load
|
|
227
|
+
images below the fold; eager-load images in the first visible category.
|
|
228
|
+
- 30 s polling is fine for a small to medium badge catalog (< 200 badges).
|
|
229
|
+
For very large catalogs, consider polling only when the badges surface
|
|
230
|
+
is in the viewport (use `IntersectionObserver`) — the SDK doesn't
|
|
231
|
+
throttle for you.
|
|
232
|
+
|
|
233
|
+
**Visitor mode: not supported.** Use [`getMissions`](../../api/classes/WSAPIMissions.md#getmissions)
|
|
234
|
+
(visitor mode supported) or [`getAchCategories`](../../api/classes/WSAPIMissions.md#getachcategories)
|
|
235
|
+
(visitor mode supported) if you need achievement data for anonymous users.
|
|
236
|
+
Calling `_smartico.vapi(lang).getBadges()` returns no usable data — visitor
|
|
237
|
+
sessions have no badge-progress path on the server.
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# UI Guide — `getMissions`
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
- Register `onUpdate` on first render and treat each invocation as
|
|
5
|
+
"discard prior list, re-render from this array". Do not merge with
|
|
6
|
+
the previous list — fields like `progress` are absolute, not deltas.
|
|
7
|
+
- Loading indicator: the first `getMissions()` is a server round-trip
|
|
8
|
+
(typically 100–500 ms); subsequent calls within the cache window
|
|
9
|
+
resolve effectively synchronously.
|
|
10
|
+
- Don't poll `getMissions()` to detect opt-in / claim completion. The
|
|
11
|
+
`onUpdate` callback is the authoritative signal.
|
|
12
|
+
- The SDK returns a flat array; there is no pre-computed status bucket
|
|
13
|
+
field. Consumers apply the bucketing filter chain below themselves.
|
|
14
|
+
|
|
15
|
+
## Status bucketing
|
|
16
|
+
Partition the array using this **priority order — first match wins**:
|
|
17
|
+
|
|
18
|
+
1. **Missed** — time window passed before completion. Test:
|
|
19
|
+
`(!is_completed) && ((is_requires_optin && is_opted_in || !is_requires_optin)`
|
|
20
|
+
` && time_limit_ms && dt_start && (dt_start + time_limit_ms) < Date.now())`
|
|
21
|
+
`|| (active_till_ts && active_till_ts < Date.now())`.
|
|
22
|
+
Sort: `position` ASC.
|
|
23
|
+
2. **Completed** — `is_completed === true`, OR for missions of type
|
|
24
|
+
`AchievementStatus.RecurringUponCompletion`,
|
|
25
|
+
`max_completion_count === completion_count` (cap reached) OR
|
|
26
|
+
`completion_count > 0` (at least one cycle completed).
|
|
27
|
+
Sort: `complete_date_ts` DESC (most recent first).
|
|
28
|
+
3. **Locked** — `is_locked === true`, OR not yet available
|
|
29
|
+
(`active_from_ts > Date.now()`). Sort: `position` ASC.
|
|
30
|
+
4. **Available (in-progress)** — none of the above.
|
|
31
|
+
Sort: `position` ASC.
|
|
32
|
+
|
|
33
|
+
Each mission belongs to exactly ONE bucket. Many UIs render these as
|
|
34
|
+
status tabs / sections. The `availability_status` field
|
|
35
|
+
([AchievementAvailabilityStatus](../../api/enumerations/AchievementAvailabilityStatus.md)) is authoritative for **timer /
|
|
36
|
+
window state** (which countdown to show, whether the window has
|
|
37
|
+
elapsed) — but it is NOT a substitute for bucketing: distinguishing
|
|
38
|
+
"locked" from "opt-in required" requires reading `is_locked` and
|
|
39
|
+
`is_opted_in` directly. An "Overview" tab curating featured +
|
|
40
|
+
in-progress + a sample locked is a common pattern.
|
|
41
|
+
|
|
42
|
+
## Mission card / tile (list view)
|
|
43
|
+
|
|
44
|
+
Fields to render on the card:
|
|
45
|
+
- `image` — documented source size 256×256 px. Render at a 1:1 aspect
|
|
46
|
+
ratio; CSS `background-image` is conventional. There is no built-in
|
|
47
|
+
placeholder for missing images — provide a neutral fallback.
|
|
48
|
+
- `name` — the title. Translated server-side. Safe to render as HTML
|
|
49
|
+
if the consumer sanitizes first.
|
|
50
|
+
- Status chip — derived from the bucket the mission falls into;
|
|
51
|
+
includes a live countdown for time-limited entries (see "time
|
|
52
|
+
formatting" below).
|
|
53
|
+
- Description (fallback chain): `description` → `unlock_mission_description`
|
|
54
|
+
(when `is_locked`) → `tasks[0].name` (when no description and tasks
|
|
55
|
+
exist).
|
|
56
|
+
- Progress bar — render only when `progress > 0` (the 0–100 integer).
|
|
57
|
+
Hide for completed recurring missions (`AchievementStatus.RecurringUponCompletion`
|
|
58
|
+
on the Completed tab) to avoid showing residual percentage from a
|
|
59
|
+
prior cycle.
|
|
60
|
+
- Reward summary (`reward` HTML) — show only when `progress === 0`.
|
|
61
|
+
For in-progress missions, the progress percentage replaces the reward.
|
|
62
|
+
- Ribbon overlay (`ribbon` field) — presets `'sale'`, `'hot'`, `'new'`,
|
|
63
|
+
`'vip'` use built-in styling; a URL means a custom **250×300 px** image.
|
|
64
|
+
Position: top-left corner overlay.
|
|
65
|
+
- "Claim" button — replaces the progress bar when
|
|
66
|
+
`is_completed && requires_prize_claim && !prize_claimed_date_ts`.
|
|
67
|
+
- "Featured" marker — alternative to ribbon for operator-marked
|
|
68
|
+
featured missions.
|
|
69
|
+
|
|
70
|
+
Fields NOT shown on the card (detail popup only):
|
|
71
|
+
- Full `description` HTML beyond a single line
|
|
72
|
+
- `sub_header`
|
|
73
|
+
- `tasks[]` (except as description fallback)
|
|
74
|
+
- `hint_text` (T&C)
|
|
75
|
+
- `related_games`
|
|
76
|
+
- Opt-in / CTA action buttons (the status chip alone communicates
|
|
77
|
+
"opt-in required" on cards; the actual button lives in the popup)
|
|
78
|
+
|
|
79
|
+
Click target: the **whole card** opens the detail popup. The Claim
|
|
80
|
+
button (when present) is the only sub-element with its own handler;
|
|
81
|
+
it must `event.stopPropagation()` so the popup doesn't also open.
|
|
82
|
+
|
|
83
|
+
## Mission detail popup
|
|
84
|
+
|
|
85
|
+
Top-to-bottom structure (omit sections whose source field is empty):
|
|
86
|
+
1. Close button.
|
|
87
|
+
2. Ribbon overlay (same as card).
|
|
88
|
+
3. Title (`name`).
|
|
89
|
+
4. Sub-header (`sub_header`).
|
|
90
|
+
5. Status chip — typically without countdown (use the dedicated
|
|
91
|
+
time-remaining block below).
|
|
92
|
+
6. Image (`image`) — render larger than the card variant.
|
|
93
|
+
7. Lock icon overlay on the image when `is_locked === true`.
|
|
94
|
+
8. Time-remaining block — "Available again in" countdown for
|
|
95
|
+
recurring waiting; "Starts on" for `active_from_ts > now`;
|
|
96
|
+
"Expired on" for missed; countdown for in-window.
|
|
97
|
+
9. Full `description` (HTML).
|
|
98
|
+
10. Action button — see the decision matrix below.
|
|
99
|
+
11. Hint / T&C — `hint_text` surfaced as an info-icon tooltip
|
|
100
|
+
(tap to expand). Not inline.
|
|
101
|
+
12. Task list — only when `tasks.length > 1` (single-task missions
|
|
102
|
+
use the task name as description fallback on the card; the
|
|
103
|
+
list is redundant). Each task row: completion indicator
|
|
104
|
+
(checkbox/circle) + `task.name`. Append "N / M" using
|
|
105
|
+
`execution_count_actual / execution_count_expected` when the
|
|
106
|
+
operator has enabled count display. Show a points stamp for
|
|
107
|
+
tasks with `points_reward > 0`.
|
|
108
|
+
13. Progress bar (conditional, same rule as card).
|
|
109
|
+
14. Reward block (`reward`).
|
|
110
|
+
15. Related games — only games where `game_public_meta.enabled === true`.
|
|
111
|
+
Render as a horizontal carousel; show navigation arrows when
|
|
112
|
+
5+ games on desktop, 4+ on mobile. Thumbnails are 1:1 aspect
|
|
113
|
+
ratio; tap a thumbnail to launch the underlying game.
|
|
114
|
+
16. Unlock description (`unlock_mission_description`) at the
|
|
115
|
+
bottom when `is_locked`.
|
|
116
|
+
|
|
117
|
+
## Action button decision matrix
|
|
118
|
+
|
|
119
|
+
Apply in priority order; **first match wins**:
|
|
120
|
+
|
|
121
|
+
1. `is_locked === true` → **no action button**. Show the unlock
|
|
122
|
+
description and a lock-icon overlay on the image.
|
|
123
|
+
2. `is_completed && requires_prize_claim && !prize_claimed_date_ts`
|
|
124
|
+
AND `prize_claim_expiration_date >= Date.now()` (claimable) →
|
|
125
|
+
**"Claim" button**. Use `claim_button_title` as the label if set,
|
|
126
|
+
otherwise a localized default. On click: call
|
|
127
|
+
`requestMissionClaimReward(id, ach_completed_id)`. Optionally
|
|
128
|
+
animate a celebration overlay after success.
|
|
129
|
+
3. Same as (2) but claim window expired (`prize_claim_expiration_date < Date.now()`) →
|
|
130
|
+
render the Claim label as **disabled**; show the formatted expiration
|
|
131
|
+
date with a "claim expired on" prefix.
|
|
132
|
+
4. `is_completed` (no claim required, or already claimed) →
|
|
133
|
+
**no action button**. Show progress / completion state.
|
|
134
|
+
5. `is_requires_optin && !is_opted_in` (and not locked) →
|
|
135
|
+
**"Opt In" button**. Disable when:
|
|
136
|
+
- `active_from_ts > Date.now()` (not yet started),
|
|
137
|
+
- the mission falls in the Missed bucket,
|
|
138
|
+
- `active_till_ts && active_till_ts < Date.now()` (expired).
|
|
139
|
+
While the opt-in request is in flight, show a loading indicator
|
|
140
|
+
on the button. The SDK does NOT enforce a single in-flight call —
|
|
141
|
+
guard against double-clicks at the UI layer (set a local
|
|
142
|
+
`optInLoading` flag on click).
|
|
143
|
+
6. `cta_text && cta_action` (and none of the above) → **CTA button**
|
|
144
|
+
labeled with `cta_text`. On click: call
|
|
145
|
+
`_smartico.dp(cta_action)`. `cta_action` is a deep-link string
|
|
146
|
+
(e.g. `'dp:deposit'`, `'dp:gf_store'`) — not a URL.
|
|
147
|
+
Disable when the mission falls in the Missed bucket or is
|
|
148
|
+
otherwise out of window.
|
|
149
|
+
7. None of the above → no action button.
|
|
150
|
+
|
|
151
|
+
Recurring missions:
|
|
152
|
+
- Waiting for next cycle (`next_recurrence_date_ts > Date.now()`) →
|
|
153
|
+
**no button**; the status chip carries the "Available again in
|
|
154
|
+
DD:HH:MM:SS" countdown.
|
|
155
|
+
- Cap reached (`max_completion_count === completion_count`) → treated
|
|
156
|
+
as Completed; no button.
|
|
157
|
+
|
|
158
|
+
## `claim_button_action` companion action
|
|
159
|
+
|
|
160
|
+
*(important caveat)*
|
|
161
|
+
|
|
162
|
+
If `claim_button_action` is set, the consumer's claim handler is
|
|
163
|
+
expected to fire BOTH the claim API call AND the deep-link action
|
|
164
|
+
**at the same time** (concurrently). The deep-link runs synchronously
|
|
165
|
+
on click — it does NOT wait for the claim RPC to succeed. Consequences:
|
|
166
|
+
- If the deep-link navigates away (e.g. opens a casino game), the
|
|
167
|
+
user may miss the claim loading animation or any error toast.
|
|
168
|
+
- If the claim RPC fails after the navigation has already started,
|
|
169
|
+
there is no rollback.
|
|
170
|
+
- Treat `claim_button_action` as a *companion navigation*, not a
|
|
171
|
+
post-claim follow-up. Common pattern:
|
|
172
|
+
`dispatch claim → _smartico.dp(claim_button_action)` in the same
|
|
173
|
+
click handler, without `await`-ing the claim.
|
|
174
|
+
- `claim_button_action` accepts both `dp:...` deep-links and full
|
|
175
|
+
`http(s)://...` URLs (URLs open in a new tab).
|
|
176
|
+
|
|
177
|
+
## Image / asset specs
|
|
178
|
+
- Mission `image`: documented source size **256×256 px**, 1:1 aspect ratio.
|
|
179
|
+
- Custom ribbon URL: documented source size **250×300 px**.
|
|
180
|
+
- Related-game thumbnails: **1:1 aspect ratio** per `game_public_meta.image`.
|
|
181
|
+
- All assets are typically rendered as CSS `background-image` (no
|
|
182
|
+
`<img>` element) so dimensions are controlled by the consumer's CSS.
|
|
183
|
+
- No built-in fallback placeholders — consumers should provide one
|
|
184
|
+
when source fields are empty.
|
|
185
|
+
|
|
186
|
+
## Status-specific visual treatments
|
|
187
|
+
- **Locked**: lock-icon overlay on the image and a "locked" CSS
|
|
188
|
+
modifier on the image container. NOT grayscale (grayscale is the
|
|
189
|
+
convention for locked badges, not locked missions).
|
|
190
|
+
- **Completed**: no tint or strikethrough on the card itself; the
|
|
191
|
+
status chip carries the completion date.
|
|
192
|
+
- **Missed**: same chip styling as Locked, different label.
|
|
193
|
+
Missions stay visible in the Missed bucket unless an operator
|
|
194
|
+
config hides expired missions after N days.
|
|
195
|
+
- **In progress**: default styling.
|
|
196
|
+
- **Opt-in required**: same as in-progress; the status chip alone
|
|
197
|
+
signals "opt-in required".
|
|
198
|
+
- **Featured**: replace the ribbon (when no `ribbon` is set) with a
|
|
199
|
+
"featured" modifier; some brands use a gold border / glow.
|
|
200
|
+
- **Claimable** (claim button visible): add a "claimable" modifier
|
|
201
|
+
to the card root to apply a highlight border or glow drawing
|
|
202
|
+
attention to the action.
|
|
203
|
+
|
|
204
|
+
## Time-remaining countdown format
|
|
205
|
+
|
|
206
|
+
Convention used by the default Smartico UI (consumers should match for
|
|
207
|
+
a consistent feel across Smartico widgets):
|
|
208
|
+
- `≤ 0` → `00:00`
|
|
209
|
+
- `< 1 hour` → `MM:SS`
|
|
210
|
+
- `< 1 day` → `HH:MM:SS`
|
|
211
|
+
- `< 1 month` → `DD:HH:MM:SS`
|
|
212
|
+
- `< 1 year` → `MO:DD:HH:MM:SS`
|
|
213
|
+
- `≥ 1 year` → `YY:MO:DD:HH:MM:SS`
|
|
214
|
+
|
|
215
|
+
Update the displayed text every 1 second while the mission is in
|
|
216
|
+
progress.
|
|
217
|
+
|
|
218
|
+
## Empty states
|
|
219
|
+
|
|
220
|
+
Show a contextual empty-state message per bucket:
|
|
221
|
+
- Completed: "No completed missions yet"
|
|
222
|
+
- Locked: "No locked missions"
|
|
223
|
+
- Others (Available / Missed): "Nothing to show yet"
|
|
224
|
+
|
|
225
|
+
## Animations / transitions
|
|
226
|
+
- List entry: brief zoom-in or fade-in per card (stagger optional).
|
|
227
|
+
- Popup open: bounce + fade-in; stagger inner elements (title, image,
|
|
228
|
+
description, actions) for a layered reveal.
|
|
229
|
+
- Progress bar fill: animate the width over ~1 second when progress
|
|
230
|
+
advances (especially in the popup; cards often render the bar
|
|
231
|
+
statically).
|
|
232
|
+
- Claim success: hide the Claim button immediately on success and
|
|
233
|
+
(optionally) overlay a confetti or celebration animation.
|
|
234
|
+
- In-flight buttons (opt-in / claim): show a loading indicator
|
|
235
|
+
(animated dots, spinner) and disable the trigger element until the
|
|
236
|
+
response arrives.
|
|
237
|
+
|
|
238
|
+
## Mobile vs desktop
|
|
239
|
+
- Mobile: single-column vertical scroll.
|
|
240
|
+
- Desktop: multi-column grid. The default Smartico UI alternates a "big"
|
|
241
|
+
and "small" card variant in a masonry pattern (every 4th in even
|
|
242
|
+
rows / every 5th in odd rows is "big"). Consumers may use any grid
|
|
243
|
+
pattern that suits their design.
|
|
244
|
+
- The detail popup is structurally identical on mobile and desktop;
|
|
245
|
+
only sizing differs.
|
|
246
|
+
|
|
247
|
+
## Performance
|
|
248
|
+
|
|
249
|
+
The list is not paginated; the SDK returns the full visible mission
|
|
250
|
+
set in one response. For very large catalogs (50+ missions visible),
|
|
251
|
+
consider client-side virtualization in the consumer UI. The official
|
|
252
|
+
tracker does NOT virtualize.
|
|
253
|
+
|
|
254
|
+
**Cache TTL**: the SDK caches the response for 30 seconds. The cache
|
|
255
|
+
is invalidated and re-fetched automatically on each of the three
|
|
256
|
+
trigger events above; `onUpdate` fires after the re-fetch completes.
|
|
257
|
+
|
|
258
|
+
**Visitor mode**: supported, with caveats.
|
|
259
|
+
- The returned list reflects the brand's proxy "visitor" user, not
|
|
260
|
+
the actual anonymous viewer. Per-user fields (`is_opted_in`,
|
|
261
|
+
`progress`, `completed_count`) reflect the proxy user's state and
|
|
262
|
+
are not meaningful for display.
|
|
263
|
+
- `onUpdate` is accepted by the API but NEVER fires in visitor mode —
|
|
264
|
+
there is no live channel, so push events are not delivered.
|
|
265
|
+
Visitor-mode consumers should re-poll `getMissions()` periodically
|
|
266
|
+
if fresh data is needed.
|
|
267
|
+
- Additional server-side caching applies on top of the SDK cache,
|
|
268
|
+
so visitor-mode data may be a few minutes stale.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# UI Guide — `requestMissionClaimReward`
|
|
2
|
+
|
|
3
|
+
## UI guidance
|
|
4
|
+
- Show a loading indicator while the promise is pending; this is a
|
|
5
|
+
server round-trip with side effects (DB writes, CRM rules) and may
|
|
6
|
+
take 200–800ms depending on configured CRM automations.
|
|
7
|
+
- Disable the claim button for the duration of the call. The local
|
|
8
|
+
`prize_claimed_date_ts` flag won't flip until the auto-refresh fires
|
|
9
|
+
after the response.
|
|
10
|
+
- Treat `err_code === 0` and `err_code === 40017` identically in the
|
|
11
|
+
UI: hide the claim button. Don't surface 40017 as an error.
|
|
12
|
+
- For `err_code === 40015` (window expired), show a dedicated message
|
|
13
|
+
referencing the original `prize_claim_expiration_date`, not a generic
|
|
14
|
+
error.
|
|
15
|
+
- For `err_code === 40016` (not completed), this should not normally
|
|
16
|
+
happen if the UI gates on `is_completed === true`. If it does, the
|
|
17
|
+
local state is stale — auto-refresh will correct it.
|
|
18
|
+
- Do NOT optimistically mark the prize as claimed before the response.
|
|
19
|
+
Authoritative state comes from the SDK's auto-refresh.
|
|
20
|
+
|
|
21
|
+
**Visitor mode**: not supported.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# UI Guide — `requestMissionOptIn`
|
|
2
|
+
|
|
3
|
+
## UI guidance
|
|
4
|
+
- Show a loading indicator (spinner, dots, button-disable) while the
|
|
5
|
+
promise is pending; opt-in is a server round-trip and may take
|
|
6
|
+
100–500ms under normal conditions.
|
|
7
|
+
- Disable the trigger element for the duration of the call to prevent
|
|
8
|
+
double-clicks. The local `is_opted_in` flag won't flip until the
|
|
9
|
+
`onUpdate` callback fires post-response.
|
|
10
|
+
- Treat `err_code === 0` and `err_code === 40010` identically in the
|
|
11
|
+
UI: hide the opt-in button. Don't surface 40010 as an error.
|
|
12
|
+
- For `err_code === 40014` (locked), show the mission's
|
|
13
|
+
`unlock_mission_description` instead of a generic error.
|
|
14
|
+
- Do NOT optimistically set `is_opted_in = true` before the response.
|
|
15
|
+
Authoritative refresh comes from the SDK's `onUpdate` invocation,
|
|
16
|
+
not from local state.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# UI Guide — `buyStoreItem`
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
Buy is a server round-trip with side effects (DB writes, balance
|
|
5
|
+
deduction, reward issuance, optional CRM rules). Expected latency
|
|
6
|
+
200–800ms depending on reward type. Always show an in-flight
|
|
7
|
+
indicator on the Buy button and disable it for the duration of the
|
|
8
|
+
call; never optimistically update the local balance / pool — wait
|
|
9
|
+
for the auto-refresh.
|
|
10
|
+
|
|
11
|
+
## Confirm modal (recommended)
|
|
12
|
+
The typical UX shows a confirm modal between the item list and the
|
|
13
|
+
Buy click: tapping a card opens the item detail modal which displays
|
|
14
|
+
the item image, name, full description, price, the user's current
|
|
15
|
+
balance, and the Buy button. The Buy button on the **card itself**
|
|
16
|
+
is optional; many implementations only show it inside the detail
|
|
17
|
+
modal. This serves both as a confirmation step and as the natural
|
|
18
|
+
place to render insufficient-balance / locked / sold-out messages
|
|
19
|
+
next to the action.
|
|
20
|
+
|
|
21
|
+
## Buy button decision matrix
|
|
22
|
+
|
|
23
|
+
Apply in priority order; **first match wins**. The state checks all
|
|
24
|
+
derive from a single `TStoreItem` (from `getStoreItems()`) plus the
|
|
25
|
+
user's current balance (from user properties).
|
|
26
|
+
|
|
27
|
+
1. `pool != null && pool <= 0` → **disabled "Sold Out" button** (or
|
|
28
|
+
hide the Buy button and show an out-of-stock badge). Auto-refresh
|
|
29
|
+
will update the state if stock is restored.
|
|
30
|
+
2. `active_till_date && Date.now() >= active_till_date` →
|
|
31
|
+
**disabled "Expired" button**. Show the formatted expiry date.
|
|
32
|
+
3. `can_buy === false` → **disabled Buy button**. Surface
|
|
33
|
+
`cant_buy_message` (operator-defined) if set; otherwise a
|
|
34
|
+
generic "Not available" caption. This catches segment locks
|
|
35
|
+
and operator-set restrictions.
|
|
36
|
+
4. User balance for the item's `purchase_type` is less than the
|
|
37
|
+
effective price (`discounted_price ?? price`) →
|
|
38
|
+
**disabled "Insufficient balance" button**. Show how many
|
|
39
|
+
units of the currency are missing (e.g. "120 points needed").
|
|
40
|
+
The button label can stay "Buy" but with a disabled visual.
|
|
41
|
+
5. A purchase is currently in flight for this item (local flag) →
|
|
42
|
+
**Buy button with loading indicator**; ignore additional clicks.
|
|
43
|
+
6. None of the above → **enabled Buy button** labelled e.g. "Buy"
|
|
44
|
+
with the price + currency icon (points / gems / diamonds) as a
|
|
45
|
+
stamp. On click: call `buyStoreItem(item.id)`; set the local
|
|
46
|
+
in-flight flag.
|
|
47
|
+
|
|
48
|
+
Variants by `purchase_type`:
|
|
49
|
+
- `'points'` — show the points icon next to the price.
|
|
50
|
+
- `'gems'` — show the gems icon.
|
|
51
|
+
- `'diamonds'` — show the diamonds icon.
|
|
52
|
+
|
|
53
|
+
If `discounted_price` is set, render both prices: the original
|
|
54
|
+
`price` with a strikethrough and the `discounted_price` highlighted.
|
|
55
|
+
|
|
56
|
+
## In-flight UX
|
|
57
|
+
- Replace the Buy button label with a loading dots / spinner
|
|
58
|
+
animation while the promise is pending. Keep the button visually
|
|
59
|
+
"pressed" / disabled.
|
|
60
|
+
- Do NOT close the detail modal during the call — closing risks the
|
|
61
|
+
user double-clicking after re-opening.
|
|
62
|
+
|
|
63
|
+
## Success UX
|
|
64
|
+
On `err_code === 0`:
|
|
65
|
+
- Close the detail modal.
|
|
66
|
+
- Show a success toast (e.g. "Item purchased successfully"). The
|
|
67
|
+
toast is the primary feedback — there is no dedicated success
|
|
68
|
+
modal for the generic case.
|
|
69
|
+
- Do NOT manually re-fetch `getStoreItems()` — the auto-refresh
|
|
70
|
+
fires.
|
|
71
|
+
- Reward-type-specific celebrations (e.g. opening a mini-game spin
|
|
72
|
+
immediately after a `'minigamespin'` purchase, navigating to the
|
|
73
|
+
bonus screen after a bonus purchase) are application choices and
|
|
74
|
+
not enforced by the SDK.
|
|
75
|
+
|
|
76
|
+
## Error UX per code
|
|
77
|
+
- For `11006` (cap reached): prefer the item's `limit_message`
|
|
78
|
+
operator copy over `err_message`.
|
|
79
|
+
- For `11014` (time-window): prefer the item's `purchase_limit_message`
|
|
80
|
+
operator copy over `err_message`. Both are localized, operator-tuned
|
|
81
|
+
strings meant for the user.
|
|
82
|
+
- For `11009` (sold out): show a dedicated "sold out" message.
|
|
83
|
+
- For `11004`: show a localized "try again later" / "not eligible"
|
|
84
|
+
message; the raw `err_message` is rarely user-friendly.
|
|
85
|
+
- For all other non-zero codes: fall back to `err_message`, or to
|
|
86
|
+
a generic "Something went wrong. Try again later." if `err_message`
|
|
87
|
+
is empty.
|
|
88
|
+
- Render the error in a dedicated error modal (closing the item
|
|
89
|
+
detail modal first) or as an inline message inside the detail
|
|
90
|
+
modal — both patterns are valid; the dedicated modal is the
|
|
91
|
+
reference implementation.
|
|
92
|
+
|
|
93
|
+
## Status-specific visual treatments on the item card
|
|
94
|
+
- `pool != null && pool <= 0` → grey-out overlay + "Out of Stock"
|
|
95
|
+
caption. When operator-enabled, unavailable items can be visually
|
|
96
|
+
greyed out entirely.
|
|
97
|
+
- `can_buy === false` → optional "cant_buy" visual treatment
|
|
98
|
+
(border / icon) + tooltip showing `cant_buy_message`.
|
|
99
|
+
- `active_till_date` set → live-updating countdown overlay,
|
|
100
|
+
format `DD:HH:MM:SS` (see below). Hide once expired and replace
|
|
101
|
+
with an "Expired" badge.
|
|
102
|
+
- `discounted_price` set → discount ribbon (corner overlay) showing
|
|
103
|
+
the percent or a custom ribbon image.
|
|
104
|
+
- `purchased_today` / `purchased_this_week` / `purchased_this_month`
|
|
105
|
+
true → optional "purchased recently" badge; useful for items
|
|
106
|
+
with a per-period cap, hints at why the Buy button might be
|
|
107
|
+
disabled.
|
|
108
|
+
|
|
109
|
+
## Countdown format
|
|
110
|
+
For items with `active_till_date`:
|
|
111
|
+
- Format `DD:HH:MM:SS`, each segment zero-padded to 2 digits.
|
|
112
|
+
- Update cadence: 1 second.
|
|
113
|
+
- Once `Date.now() >= active_till_date`, hide the countdown and
|
|
114
|
+
mark the item as expired (disabled Buy button, "Expired" badge).
|
|
115
|
+
|
|
116
|
+
## Animations
|
|
117
|
+
- In-flight: dots / spinner animation on the Buy button.
|
|
118
|
+
- Success: a celebratory animation (confetti, coin shower) for
|
|
119
|
+
high-value items is a common pattern but not enforced by the SDK.
|
|
120
|
+
The default minimal UX is the success toast.
|
|
121
|
+
|
|
122
|
+
## Mobile vs desktop
|
|
123
|
+
The buy flow is identical on mobile and desktop; the layout
|
|
124
|
+
differences live in the parent list view (see `getStoreItems` for
|
|
125
|
+
grid / row variants). The detail modal and Buy button behaviour
|
|
126
|
+
are the same on both.
|
|
127
|
+
|
|
128
|
+
## Empty states
|
|
129
|
+
Not applicable to this method (returns a single result). For the
|
|
130
|
+
list-level empty / loading / error states, see the `getStoreItems`
|
|
131
|
+
documentation.
|
|
132
|
+
|
|
133
|
+
**Visitor mode**: not supported.
|