@smartico/public-api 0.0.379 → 0.0.381

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 (219) hide show
  1. package/.nvmrc +1 -1
  2. package/CLAUDE.md +103 -0
  3. package/README.md +53 -2
  4. package/dist/Base/ClassId.d.ts +2 -0
  5. package/dist/Jackpots/JackpotDetails.d.ts +14 -15
  6. package/dist/Jackpots/JackpotPot.d.ts +10 -6
  7. package/dist/Raffle/GetRaffleWonPrizesRequest.d.ts +9 -0
  8. package/dist/Raffle/GetRaffleWonPrizesResponse.d.ts +52 -0
  9. package/dist/Raffle/index.d.ts +2 -0
  10. package/dist/SmarticoAPI.d.ts +6 -1
  11. package/dist/SmarticoLib/index.d.ts +5 -1
  12. package/dist/Store/BuyStoreItemErrorCode.d.ts +9 -1
  13. package/dist/Tournaments/TournamentRegistrationError.d.ts +3 -1
  14. package/dist/WSAPI/WSAPI.d.ts +13 -1235
  15. package/dist/WSAPI/WSAPIAvatars.d.ts +296 -0
  16. package/dist/WSAPI/WSAPIBase.d.ts +71 -0
  17. package/dist/WSAPI/WSAPIBonuses.d.ts +240 -0
  18. package/dist/WSAPI/WSAPIClans.d.ts +388 -0
  19. package/dist/WSAPI/WSAPIGamePick.d.ts +593 -0
  20. package/dist/WSAPI/WSAPIGeneral.d.ts +172 -0
  21. package/dist/WSAPI/WSAPIInbox.d.ts +615 -0
  22. package/dist/WSAPI/WSAPIJackpots.d.ts +344 -0
  23. package/dist/WSAPI/WSAPILeaderBoard.d.ts +139 -0
  24. package/dist/WSAPI/WSAPIMiniGames.d.ts +510 -0
  25. package/dist/WSAPI/WSAPIMissions.d.ts +517 -0
  26. package/dist/WSAPI/WSAPIRaffles.d.ts +643 -0
  27. package/dist/WSAPI/WSAPIStore.d.ts +656 -0
  28. package/dist/WSAPI/WSAPITournaments.d.ts +501 -0
  29. package/dist/WSAPI/WSAPITypes.d.ts +376 -205
  30. package/dist/WSAPI/WSAPIUser.d.ts +533 -0
  31. package/dist/index.js +6745 -1661
  32. package/dist/index.js.map +1 -1
  33. package/dist/index.modern.mjs +6324 -1342
  34. package/dist/index.modern.mjs.map +1 -1
  35. package/docs/Callbacks.md +162 -0
  36. package/docs/SmarticoObject.md +445 -0
  37. package/docs/Transforms.md +99 -0
  38. package/docs/api/README.md +82 -2
  39. package/docs/api/classes/WSAPIAvatars.md +344 -0
  40. package/docs/api/classes/WSAPIBonuses.md +271 -0
  41. package/docs/api/classes/WSAPIClans.md +436 -0
  42. package/docs/api/classes/WSAPIGamePick.md +710 -0
  43. package/docs/api/classes/WSAPIGeneral.md +212 -0
  44. package/docs/api/classes/WSAPIInbox.md +801 -0
  45. package/docs/api/classes/WSAPIJackpots.md +439 -0
  46. package/docs/api/classes/WSAPILeaderBoard.md +157 -0
  47. package/docs/api/classes/WSAPIMiniGames.md +612 -0
  48. package/docs/api/classes/WSAPIMissions.md +590 -0
  49. package/docs/api/classes/WSAPIRaffles.md +773 -0
  50. package/docs/api/classes/WSAPIStore.md +719 -0
  51. package/docs/api/classes/WSAPITournaments.md +571 -0
  52. package/docs/api/classes/WSAPIUser.md +637 -0
  53. package/docs/api/documents/Callbacks.md +162 -0
  54. package/docs/api/documents/SmarticoObject.md +445 -0
  55. package/docs/api/documents/Transforms.md +99 -0
  56. package/docs/api/enumerations/AchievementClaimPeriodTypeId.md +13 -0
  57. package/docs/api/enumerations/BonusStatus-1.md +11 -6
  58. package/docs/api/enumerations/BuyStoreItemErrorCode.md +32 -0
  59. package/docs/api/enumerations/JoinClanErrorCode.md +81 -0
  60. package/docs/api/enumerations/TournamentRegistrationError.md +12 -0
  61. package/docs/api/functions/clanTournamentPlayersTransform.md +13 -0
  62. package/docs/api/interfaces/AvatarCustomized.md +25 -0
  63. package/docs/api/interfaces/AvatarDefinition.md +94 -0
  64. package/docs/api/interfaces/AvatarPrompt.md +45 -0
  65. package/docs/api/interfaces/BonusMetaMap-1.md +4 -1
  66. package/docs/api/interfaces/BonusTemplateMetaMap-1.md +9 -4
  67. package/docs/api/interfaces/Clan.md +4 -0
  68. package/docs/api/interfaces/ClanInfo.md +109 -0
  69. package/docs/api/interfaces/ClanMember.md +53 -0
  70. package/docs/api/interfaces/ClanPrizePublicMeta.md +41 -0
  71. package/docs/api/interfaces/ClanPrizeStructureEntry.md +7 -1
  72. package/docs/api/interfaces/ClanPublicMeta.md +4 -0
  73. package/docs/api/interfaces/ClanTournamentPlayerRaw.md +67 -0
  74. package/docs/api/interfaces/GemsDiamondsLog.md +43 -0
  75. package/docs/api/interfaces/GetClanInfoResponse.md +61 -0
  76. package/docs/api/interfaces/GetClanTournamentPlayersResponse.md +81 -0
  77. package/docs/api/interfaces/GetRaffleWonPrizesRequest.md +59 -0
  78. package/docs/api/interfaces/GetRaffleWonPrizesResponse.md +95 -0
  79. package/docs/api/interfaces/InboxMarkMessageAction.md +10 -2
  80. package/docs/api/interfaces/JackpotDetails.md +14 -15
  81. package/docs/api/interfaces/JackpotPot.md +9 -6
  82. package/docs/api/interfaces/LeaderBoardDetailsT.md +14 -8
  83. package/docs/api/interfaces/LeaderBoardUserT.md +8 -5
  84. package/docs/api/interfaces/LeaderBoardsRewardsT.md +7 -0
  85. package/docs/api/interfaces/PointsLog.md +43 -0
  86. package/docs/api/interfaces/RaffleWonPrize.md +59 -0
  87. package/docs/api/interfaces/RaffleWonPrizePublicMeta.md +27 -0
  88. package/docs/api/interfaces/RaffleWonPrizeUser.md +43 -0
  89. package/docs/api/interfaces/SAWTemplateUI.md +10 -0
  90. package/docs/api/interfaces/TAchCategory.md +14 -4
  91. package/docs/api/interfaces/TAvatarCustomized.md +4 -4
  92. package/docs/api/interfaces/TAvatarDefinition.md +13 -14
  93. package/docs/api/interfaces/TAvatarPrompt.md +6 -6
  94. package/docs/api/interfaces/TBonus.md +16 -8
  95. package/docs/api/interfaces/TBuyStoreItemResult.md +4 -11
  96. package/docs/api/interfaces/TClaimBonusResult.md +5 -5
  97. package/docs/api/interfaces/TClan.md +8 -4
  98. package/docs/api/interfaces/TClanInfo.md +47 -0
  99. package/docs/api/interfaces/TClanJoinResult.md +11 -1
  100. package/docs/api/interfaces/TClanTournamentPlayer.md +55 -0
  101. package/docs/api/interfaces/TClanTournamentPlayersResult.md +27 -0
  102. package/docs/api/interfaces/TClans.md +3 -1
  103. package/docs/api/interfaces/TGetTranslations.md +4 -0
  104. package/docs/api/interfaces/TInboxMessage.md +14 -6
  105. package/docs/api/interfaces/TInboxMessageBody.md +18 -12
  106. package/docs/api/interfaces/TLevel.md +17 -20
  107. package/docs/api/interfaces/TLevelCurrent.md +19 -18
  108. package/docs/api/interfaces/TMiniGamePlayBatchResult.md +14 -6
  109. package/docs/api/interfaces/TMiniGamePlayResult.md +9 -4
  110. package/docs/api/interfaces/TMissionClaimRewardResult.md +6 -3
  111. package/docs/api/interfaces/TMissionOptInResult.md +6 -3
  112. package/docs/api/interfaces/TMissionOrBadge.md +5 -4
  113. package/docs/api/interfaces/TRaffleOptinResponse.md +4 -2
  114. package/docs/api/interfaces/TSegmentCheckResult.md +10 -0
  115. package/docs/api/interfaces/TSetAvatarResult.md +3 -3
  116. package/docs/api/interfaces/TStoreItem.md +11 -1
  117. package/docs/api/interfaces/TTournament.md +1 -1
  118. package/docs/api/interfaces/TTournamentDetailed.md +9 -1
  119. package/docs/api/interfaces/TTournamentRegistrationResult.md +4 -2
  120. package/docs/api/interfaces/TUICustomSection.md +23 -19
  121. package/docs/api/interfaces/TUserProfile.md +18 -16
  122. package/docs/api/interfaces/TransformedRaffleClaimPrizeResponse.md +6 -2
  123. package/docs/api/interfaces/UserAchievement.md +1 -1
  124. package/docs/api/interfaces/UserLevelExtraCountersT.md +9 -2
  125. package/docs/api/type-aliases/ActivityLogEntry.md +3 -0
  126. package/docs/native/ADDING_METHODS.md +7 -2
  127. package/docs/native/PROTOCOL.md +337 -0
  128. package/docs/ui/avatars/UIGuide_getAvatarPrompts.md +100 -0
  129. package/docs/ui/avatars/UIGuide_getAvatarsCustomized.md +90 -0
  130. package/docs/ui/avatars/UIGuide_getAvatarsList.md +105 -0
  131. package/docs/ui/avatars/UIGuide_setAvatar.md +118 -0
  132. package/docs/ui/bonuses/UIGuide_claimBonus.md +144 -0
  133. package/docs/ui/bonuses/UIGuide_getBonuses.md +176 -0
  134. package/docs/ui/clans/UIGuide_getClanInfo.md +153 -0
  135. package/docs/ui/clans/UIGuide_getClans.md +172 -0
  136. package/docs/ui/clans/UIGuide_joinClan.md +196 -0
  137. package/docs/ui/gamepick/UIGuide_gamePickGetActiveRound.md +99 -0
  138. package/docs/ui/gamepick/UIGuide_gamePickGetActiveRounds.md +76 -0
  139. package/docs/ui/gamepick/UIGuide_gamePickGetBoard.md +81 -0
  140. package/docs/ui/gamepick/UIGuide_gamePickGetGameInfo.md +78 -0
  141. package/docs/ui/gamepick/UIGuide_gamePickGetHistory.md +55 -0
  142. package/docs/ui/gamepick/UIGuide_gamePickGetRoundInfoForUser.md +71 -0
  143. package/docs/ui/gamepick/UIGuide_gamePickGetUserInfo.md +64 -0
  144. package/docs/ui/gamepick/UIGuide_gamePickSubmitSelection.md +103 -0
  145. package/docs/ui/gamepick/UIGuide_gamePickSubmitSelectionQuiz.md +67 -0
  146. package/docs/ui/general/UIGuide_getCustomSections.md +137 -0
  147. package/docs/ui/general/UIGuide_getRelatedItemsForGame.md +140 -0
  148. package/docs/ui/inbox/UIGuide_deleteInboxMessage.md +135 -0
  149. package/docs/ui/inbox/UIGuide_getInboxMessageBody.md +133 -0
  150. package/docs/ui/inbox/UIGuide_getInboxMessages.md +186 -0
  151. package/docs/ui/inbox/UIGuide_markInboxMessageAsRead.md +122 -0
  152. package/docs/ui/inbox/UIGuide_markUnmarkInboxMessageAsFavorite.md +84 -0
  153. package/docs/ui/jackpots/UIGuide_getJackpotEligibleGames.md +73 -0
  154. package/docs/ui/jackpots/UIGuide_getJackpotWinners.md +83 -0
  155. package/docs/ui/jackpots/UIGuide_jackpotGet.md +138 -0
  156. package/docs/ui/jackpots/UIGuide_jackpotOptIn.md +85 -0
  157. package/docs/ui/jackpots/UIGuide_jackpotOptOut.md +65 -0
  158. package/docs/ui/leaderboard/UIGuide_getLeaderBoard.md +166 -0
  159. package/docs/ui/minigames/UIGuide_getMiniGames.md +153 -0
  160. package/docs/ui/minigames/UIGuide_getMiniGamesHistory.md +90 -0
  161. package/docs/ui/minigames/UIGuide_playMiniGame.md +150 -0
  162. package/docs/ui/minigames/UIGuide_playMiniGameBatch.md +128 -0
  163. package/docs/ui/missions/UIGuide_getAchCategories.md +17 -0
  164. package/docs/ui/missions/UIGuide_getBadges.md +237 -0
  165. package/docs/ui/missions/UIGuide_getMissions.md +268 -0
  166. package/docs/ui/missions/UIGuide_requestMissionClaimReward.md +21 -0
  167. package/docs/ui/missions/UIGuide_requestMissionOptIn.md +16 -0
  168. package/docs/ui/raffles/UIGuide_claimRafflePrize.md +99 -0
  169. package/docs/ui/raffles/UIGuide_getRaffleDrawRun.md +117 -0
  170. package/docs/ui/raffles/UIGuide_getRaffleDrawRunsHistory.md +92 -0
  171. package/docs/ui/raffles/UIGuide_getRaffleWonPrizes.md +120 -0
  172. package/docs/ui/raffles/UIGuide_getRaffles.md +175 -0
  173. package/docs/ui/raffles/UIGuide_requestRaffleOptin.md +114 -0
  174. package/docs/ui/store/UIGuide_buyStoreItem.md +133 -0
  175. package/docs/ui/store/UIGuide_getStoreCategories.md +19 -0
  176. package/docs/ui/store/UIGuide_getStoreItems.md +264 -0
  177. package/docs/ui/store/UIGuide_getStorePurchasedItems.md +122 -0
  178. package/docs/ui/tournaments/UIGuide_getClanTournamentPlayers.md +140 -0
  179. package/docs/ui/tournaments/UIGuide_getTournamentInstanceInfo.md +265 -0
  180. package/docs/ui/tournaments/UIGuide_getTournamentsList.md +236 -0
  181. package/docs/ui/tournaments/UIGuide_registerInTournament.md +156 -0
  182. package/docs/ui/user/UIGuide_getActivityLog.md +130 -0
  183. package/docs/ui/user/UIGuide_getLevels.md +167 -0
  184. package/docs/ui/user/UIGuide_getUserProfile.md +213 -0
  185. package/package.json +3 -3
  186. package/scripts/postdoc-cleanup.js +195 -0
  187. package/src/Base/ClassId.ts +2 -0
  188. package/src/Jackpots/JackpotDetails.ts +14 -15
  189. package/src/Jackpots/JackpotPot.ts +10 -6
  190. package/src/Missions/MissionsUtils.ts +5 -2
  191. package/src/Raffle/GetRaffleWonPrizesRequest.ts +12 -0
  192. package/src/Raffle/GetRaffleWonPrizesResponse.ts +56 -0
  193. package/src/Raffle/index.ts +3 -1
  194. package/src/SmarticoAPI.ts +14 -1
  195. package/src/SmarticoLib/index.ts +4 -0
  196. package/src/Store/BuyStoreItemErrorCode.ts +8 -0
  197. package/src/Store/StoreItem.ts +1 -0
  198. package/src/Store/StoreItemPurchased.ts +1 -0
  199. package/src/Store/StoreItemType.ts +1 -1
  200. package/src/Tournaments/TournamentRegistrationError.ts +4 -0
  201. package/src/WSAPI/WSAPI.ts +27 -2007
  202. package/src/WSAPI/WSAPIAvatars.ts +345 -0
  203. package/src/WSAPI/WSAPIBase.ts +93 -0
  204. package/src/WSAPI/WSAPIBonuses.ts +268 -0
  205. package/src/WSAPI/WSAPIClans.ts +417 -0
  206. package/src/WSAPI/WSAPIGamePick.ts +667 -0
  207. package/src/WSAPI/WSAPIGeneral.ts +199 -0
  208. package/src/WSAPI/WSAPIInbox.ts +711 -0
  209. package/src/WSAPI/WSAPIJackpots.ts +448 -0
  210. package/src/WSAPI/WSAPILeaderBoard.ts +154 -0
  211. package/src/WSAPI/WSAPIMiniGames.ts +597 -0
  212. package/src/WSAPI/WSAPIMissions.ts +572 -0
  213. package/src/WSAPI/WSAPIRaffles.ts +703 -0
  214. package/src/WSAPI/WSAPIStore.ts +732 -0
  215. package/src/WSAPI/WSAPITournaments.ts +541 -0
  216. package/src/WSAPI/WSAPITypes.ts +376 -207
  217. package/src/WSAPI/WSAPIUser.ts +606 -0
  218. package/tsconfig.json +26 -2
  219. package/docs/api/classes/WSAPI.md +0 -1927
@@ -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.
@@ -0,0 +1,19 @@
1
+ # UI Guide — `getStoreCategories`
2
+
3
+ ## UI guidance
4
+ - Treat categories as relatively static. Safe to fetch once on store-page entry and
5
+ reuse for the lifetime of the view; the SDK's 30 s cache handles short-term re-calls.
6
+ - Sort by `order` ascending before rendering. Items with the same `order` should fall
7
+ back to `name` or `id` for stable ordering.
8
+ - For tab UIs (the common store layout), generate one tab per non-empty category.
9
+ The reference UI also appends the count of items in parentheses on each tab label
10
+ (e.g. `Bonuses (12)`) — compute the count by intersecting `getStoreItems()` with
11
+ each category id.
12
+ - If you want an "All items" tab, generate it client-side; the server does not return one.
13
+ - Loading indicator: skip on cache hit; show a brief shimmer on cold fetch (typically
14
+ 100–300 ms).
15
+ - Empty state: when this method returns `[]`, render the store as a flat grid (no tabs,
16
+ no section headers) over `getStoreItems()`.
17
+
18
+ **Visitor mode**: supported. Use `_smartico.vapi(lang).getStoreCategories()` to fetch
19
+ categories with `name` translated to the given language without an authenticated user.
@@ -0,0 +1,264 @@
1
+ # UI Guide — `getStoreItems`
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.
6
+ - Loading indicator: the first `getStoreItems()` is a server round-trip
7
+ (typically 100–500 ms); subsequent calls within the 30 s cache window
8
+ resolve effectively synchronously.
9
+ - The SDK returns a flat array; tabs/sections/filters are applied
10
+ client-side using `category_ids`, `custom_section_id`, and
11
+ `only_in_custom_section`.
12
+ - The catalog card UI is the SAME for all `type` values. Type-specific
13
+ behavior (lootbox reveal, bonus activation toast, SAW redirect) is
14
+ the consumer's responsibility AFTER the purchase succeeds, not on
15
+ the catalog tile itself.
16
+
17
+ ## List view organization
18
+ - **Categories**: render `getStoreCategories()` as tabs/chips. For each
19
+ tab, filter the catalog to items whose `category_ids` includes the
20
+ active category id. An item present in multiple categories appears
21
+ under each.
22
+ - **Custom sections**: the operator can group items into
23
+ custom_section_id buckets that live on a separate route from the main
24
+ store. Items with `only_in_custom_section: true` MUST be hidden from
25
+ the main store and appear only inside their `custom_section_id`.
26
+ Items WITHOUT that flag (but with a `custom_section_id`) appear in
27
+ both places.
28
+ - **Sorting** (within a category): three modes, operator-configurable
29
+ default order:
30
+ 1. "Natural" — a composite sort string from operator config
31
+ (commonly `canbuy desc, priority asc, price asc`). Sorts buyable
32
+ items above non-buyable, then by `priority` (lower first),
33
+ breaking ties by `price`.
34
+ 2. "Price" — by numeric `price`, ASC or DESC.
35
+ 3. "Name" — alphabetic by `name`, ASC or DESC.
36
+ Default is "Natural". Consumers may expose toggles.
37
+ - **No status bucketing** (unlike missions). Sold-out / can't-buy
38
+ items are intermixed with buyable items; the "Natural" sort just
39
+ pushes them lower. Render them with a status overlay (see status
40
+ treatments below) rather than putting them in a separate tab.
41
+ - **Search**: optional client-side substring match on `name`
42
+ (case-insensitive). The SDK does not paginate.
43
+ - **No pagination**: the SDK returns the full visible catalog in one
44
+ response. For very large catalogs (50+ items), consider client-side
45
+ virtualization. The default Smartico UI does NOT virtualize.
46
+
47
+ ## Item card / tile (list view)
48
+
49
+ Fields to render on the card:
50
+ - `image` — documented source size **256×256 px**, 1:1 aspect ratio.
51
+ CSS `background-image` or `<img>` is conventional.
52
+ - `name` — the title. Pre-translated server-side. Safe to render as
53
+ HTML if the consumer sanitizes first.
54
+ - Price block — `price` rendered alongside a currency icon driven by
55
+ `purchase_type` (`'points'` / `'gems'` / `'diamonds'`). When
56
+ `discounted_price` is set, render `discounted_price` as the active
57
+ price and strike-through `price`.
58
+ - Ribbon overlay (`ribbon` field) — presets `'sale'`, `'hot'`, `'new'`,
59
+ `'vip'` use built-in styling; any other string is a custom URL
60
+ (250×300 px). Position: top-left corner overlay.
61
+ - Discount ribbon (`discount_price_ribbon`) — only when set. Presets
62
+ `'10' | '15' | '20' | '25' | '50'` use built-in percent-styled
63
+ ribbons; the literal `'custom'` uses `custom_ribbon_image` (250×300 px)
64
+ as the artwork. Position: top-right corner overlay (or opposite the
65
+ item ribbon).
66
+ - Stock indicator — when `pool !== null`:
67
+ - `pool > 0`: render "In stock — N left" (using
68
+ `pool` as the count).
69
+ - `pool === 0`: render "Out of stock" / "Sold out" overlay
70
+ and grey-out the card if the operator enabled the
71
+ `grey_out_unavailable_items` config flag.
72
+ When `pool === null`, render no stock indicator (unlimited supply).
73
+ - Countdown timer — render only when `active_till_date` is set AND the
74
+ operator enabled the per-item `show_timer` flag. Format below.
75
+ - Buy button — labeled with the "Buy Now" CTA, the price, and the
76
+ currency icon. Disabled / greyed when `can_buy === false`, when the
77
+ user can't afford the item (live balance < `price`), or when
78
+ `pool === 0`.
79
+
80
+ Fields NOT shown on the card (detail popup only):
81
+ - Full `description` HTML (cards typically show just the title)
82
+ - `hint_text` (T&C)
83
+ - `related_games`
84
+ - `related_item_ids`
85
+ - `cant_buy_message` (the verbose explanation; show on the popup or as
86
+ a toast when the Buy button is clicked while disabled)
87
+ - `limit_message` / `purchase_limit_message` (surfaced post-attempt via
88
+ the buy error toast, not pre-emptively on the card)
89
+
90
+ Click target: the **whole card** opens the detail popup. The Buy button
91
+ inside the popup is where the purchase is actually dispatched — there
92
+ is no inline buy from the card in the default Smartico UI.
93
+
94
+ ## Detail popup
95
+
96
+ Top-to-bottom structure (omit sections whose source field is empty):
97
+ 1. Close button.
98
+ 2. Item ribbon overlay (same as card).
99
+ 3. Discount ribbon overlay (same as card).
100
+ 4. Title (`name`).
101
+ 5. Countdown timer (when `active_till_date && show_timer`).
102
+ 6. Stock indicator ("In stock — N left" / "Out of stock"), with
103
+ grey-out applied to the image when the item is unbuyable AND the
104
+ operator enabled `grey_out_unavailable_items`.
105
+ 7. Image (`image`) — render larger than the card variant.
106
+ 8. Full `description` (HTML).
107
+ 9. Insufficient-balance line — when the live balance for
108
+ `purchase_type` is less than `price`, render a localized
109
+ "You're short by N {points|gems|diamonds}" copy with the deficit
110
+ computed client-side (`price − balance`).
111
+ 10. `cant_buy_message` — when `can_buy === false` and there's a
112
+ reason string (e.g. "VIP only").
113
+ 11. Buy button block. Disabled state combines the three conditions
114
+ above. While the purchase RPC is in flight, replace the inner
115
+ price/CTA with a loading dots animation.
116
+ 12. T&C `hint_text` — surfaced as an info-icon tooltip below the
117
+ Buy button (tap to expand). Not inline.
118
+ 13. Related items / related games carousel (collapsed by default,
119
+ expandable). `related_item_ids` resolves against the main
120
+ catalog; `related_games` filtered to those with
121
+ `game_public_meta.enabled === true`. Show navigation arrows when
122
+ 5+ items on desktop, 4+ on mobile.
123
+
124
+ ## Action button decision matrix
125
+
126
+ *(Buy button)*
127
+
128
+ Apply in priority order; first match wins:
129
+
130
+ 1. **Purchase RPC in flight** — show loading dots over the price/CTA,
131
+ keep the button disabled until the response arrives. Guard
132
+ double-clicks at the UI layer.
133
+ 2. `pool === 0` → **"Sold out"** state; button disabled. The card and
134
+ image may be greyed (operator config).
135
+ 3. `can_buy === false` → button disabled. Surface `cant_buy_message`
136
+ (or `limit_message`) on the popup as the explanation.
137
+ 4. Balance < `price` for the relevant `purchase_type` → button
138
+ disabled. Surface a localized insufficient-balance line with the
139
+ deficit.
140
+ 5. All clear → **"Buy Now"** button enabled. On click: call
141
+ [buyStoreItem](../../api/classes/WSAPIStore.md#buystoreitem)(`id`) and show loading dots until the
142
+ response arrives.
143
+
144
+ After a successful purchase ([buyStoreItem](../../api/classes/WSAPIStore.md#buystoreitem) `err_code === 0`):
145
+ - Close the popup, show a success toast.
146
+ - The SDK auto-fires `onUpdate` with the refreshed catalog — re-render
147
+ from that array, do NOT manually decrement the local `pool`.
148
+
149
+ On a failed purchase, branch on `err_code` (see [buyStoreItem](../../api/classes/WSAPIStore.md#buystoreitem)
150
+ for the full error code table — codes `11000` / `11011` / `11012` for
151
+ insufficient balance per currency, `11006` for per-period cap reached,
152
+ `11009` for pool emptied between catalog fetch and buy, `11014` for
153
+ purchase-limit hit, etc.). Surface `purchase_limit_message` for the
154
+ limit-hit case, `limit_message` for the cap-reached case; fall back to
155
+ `err_message` otherwise.
156
+
157
+ ## Price rendering per `purchase_type`
158
+ - **`'points'`**: render `price` with a "points" currency glyph; compare
159
+ against the user's `ach_points_balance`.
160
+ - **`'gems'`**: render with a "gems" glyph; compare against
161
+ `ach_gems_balance`.
162
+ - **`'diamonds'`**: render with a "diamonds" glyph; compare against
163
+ `ach_diamonds_balance`.
164
+ - When `discounted_price` is set: the **discounted** value is the
165
+ active price (used for affordability checks); `price` becomes the
166
+ strikethrough "before" value.
167
+ - All three balances update server-side as the user plays and arrive
168
+ on the user's gamification profile asynchronously — keep them live
169
+ without polling.
170
+
171
+ ## Image / asset specs
172
+ - `image` (item icon): **256×256 px**, 1:1 aspect ratio.
173
+ - `ribbon` (when a URL): **250×300 px** custom artwork.
174
+ - `custom_ribbon_image` (discount ribbon when `discount_price_ribbon === 'custom'`):
175
+ **250×300 px**.
176
+ - Related game thumbnails: **1:1 aspect ratio** per
177
+ `game_public_meta.image`.
178
+ - No built-in fallback placeholders — consumers should provide one when
179
+ source fields are empty.
180
+
181
+ ## Status-specific visual treatments
182
+ - **Sold out** (`pool === 0`): "Out of stock" badge on the card; image
183
+ greyscaled when the operator enabled `grey_out_unavailable_items`.
184
+ Buy button disabled.
185
+ - **Cannot afford** (balance < `price` for `purchase_type`): Buy button
186
+ disabled (greyed); the rest of the card is full-color. Detail popup
187
+ adds the deficit line. The card image is greyscaled only when the
188
+ operator enabled `grey_out_unavailable_items`.
189
+ - **Not eligible** (`can_buy === false`): same as cannot-afford
190
+ visually; the explanation source is `cant_buy_message` (popup) /
191
+ `limit_message` (post-buy-attempt).
192
+ - **Discount active** (`discounted_price` set): show the discount
193
+ ribbon overlay and the strikethrough+new-price treatment.
194
+ - **New item** (operator marked `ribbon === 'new'` OR client-side
195
+ tracking of newly-added ids in localStorage): "New" ribbon preset.
196
+ The SDK does not track "newness" — the operator's `ribbon` field is
197
+ the source of truth.
198
+ - **Flash sale** (`active_till_date && show_timer`): countdown timer
199
+ on the card and popup.
200
+
201
+ ## Countdown / timing format
202
+
203
+ *(for `active_till_date`)*
204
+
205
+ Convention used by the default Smartico UI:
206
+ - `DD:HH:MM:SS` (zero-padded to 2 digits each) when the timer is set.
207
+ - Update the displayed text every **1 second**.
208
+ - When the timer reaches 0, the item disappears from the catalog on
209
+ the next refresh (server filters it out). The consumer should NOT
210
+ need a separate "expired" state for catalog items.
211
+
212
+ ## Empty / loading / error states
213
+ - **Empty catalog**: "No store items" message + illustration.
214
+ - **Empty category** (after filter): same copy or a category-scoped
215
+ variant.
216
+ - **Empty search**: no-results state with the search term.
217
+ - **Loading**: spinner / skeleton until the first response. Subsequent
218
+ `onUpdate` refreshes are fast enough to render in-place without a
219
+ skeleton.
220
+ - **Buy error**: surface `purchase_limit_message` (for
221
+ `err_code === 11014`), `limit_message` (for `err_code === 11006`), or
222
+ the server `err_message` as a modal/toast.
223
+
224
+ ## Animations / transitions
225
+ - List entry: stagger-fade-in per card (the default Smartico UI uses a
226
+ ~100 ms stagger per index).
227
+ - Popup open: bounce + fade-in on title/image; fade-in on description,
228
+ stock line, and balance line.
229
+ - Buy button in flight: loading dots overlay; disable underlying CTA.
230
+ - Purchase success: close popup, show success toast; the auto-refresh
231
+ re-renders the catalog (pool decrements, `can_buy` re-evaluates).
232
+ - Card hover (desktop): optional scale/shadow per consumer's design.
233
+
234
+ ## Mobile vs desktop
235
+ - Mobile: vertical scroll; the default Smartico UI uses a 2-column grid
236
+ for the main store and a list layout for purchase history.
237
+ - Desktop: wider multi-column grid. Related-items carousel shows
238
+ navigation arrows at 5+ items on desktop vs 4+ on mobile.
239
+ - The detail popup is structurally identical on both; only sizing
240
+ differs.
241
+
242
+ ## Performance
243
+ - No pagination. The SDK returns the full visible catalog in one
244
+ response.
245
+ - For very large catalogs (50+ items) consider client-side
246
+ virtualization. The default Smartico UI does NOT virtualize.
247
+ - The 30-second SDK cache makes intra-session re-renders free; do NOT
248
+ poll `getStoreItems()`.
249
+
250
+ **Cache TTL**: the SDK caches the response for 30 seconds. The cache is
251
+ invalidated and re-fetched automatically after every `buyStoreItem(...)`
252
+ call resolves for the current user. It is NOT invalidated by other
253
+ users' purchases, by operator BO edits, or by `active_till_date`
254
+ elapsing while the page is open.
255
+
256
+ **Visitor mode**: supported, with caveats.
257
+ - The catalog reflects the brand's proxy "visitor" user, not the actual
258
+ anonymous viewer. Per-user fields (`can_buy`, `pool` decrements,
259
+ `purchased_*` flags) reflect the proxy user's state and are not
260
+ meaningful for display.
261
+ - `onUpdate` is accepted but NEVER fires in visitor mode — there is no
262
+ live channel, so push events are not delivered.
263
+ - [buyStoreItem](../../api/classes/WSAPIStore.md#buystoreitem) is not supported in visitor mode; the Buy button
264
+ should deep-link to a login/registration flow instead.
@@ -0,0 +1,122 @@
1
+ # UI Guide — `getStorePurchasedItems`
2
+
3
+ ## Overview
4
+ Loading indicator recommended on cold fetch (no cache hit, typical
5
+ 100–500 ms latency). The default Smartico UI renders a full-screen spinner
6
+ while the first page is in flight, then a fade-in on the list. On load-
7
+ more pagination, the recommended pattern is to hide the "Load more"
8
+ button while the next page is fetching rather than showing a separate
9
+ spinner.
10
+
11
+ ## List view
12
+ - Flat newest-first list. The default Smartico UI does NOT group by date
13
+ (no "Today" / "This Week" / "Older" headers) and does NOT offer
14
+ category / type / status filter chips. If your design needs date
15
+ bucketing, use the SDK-provided `purchased_today` /
16
+ `purchased_this_week` / `purchased_this_month` booleans for grouping.
17
+ - Pagination UI: render a "Load more" button below the list when the
18
+ previous page returned exactly `limit` items. Hide it once a page
19
+ returns fewer than `limit` rows. The default Smartico UI uses an explicit
20
+ button; infinite scroll is a valid alternative but not the reference
21
+ pattern.
22
+ - Stagger animation: the default Smartico UI fades each row in with a
23
+ 100 ms incremental `animationDelay` (row 0 at 0 ms, row 1 at 100 ms,
24
+ etc.) for a cascade effect on the first page. Optional.
25
+
26
+ ## Item card (history row)
27
+ Two-column desktop / single-column-with-footer mobile layout. Rendered
28
+ fields, top-to-bottom:
29
+ 1. Item image (`image`) on the left, 1:1 aspect (256×256 source spec,
30
+ typically rendered ~64–96 px on screen via CSS).
31
+ 2. Stock indicator — only when `pool !== null`. `pool > 0` shows
32
+ "In stock — N left"; `pool === 0` shows "Out of stock".
33
+ (This reflects CURRENT stock for "Buy Again" context, not stock at
34
+ purchase time.)
35
+ 3. Item name (`name`) — HTML, sanitize to plain text or render with
36
+ `dangerouslySetInnerHTML` after trusted sanitization.
37
+ 4. Price stamp — `purchase_points_amount` paired with the currency
38
+ icon implied by `purchase_type`. This is the historical amount
39
+ paid, NOT the current item `price`.
40
+ 5. Purchase date — `purchase_ts` formatted via
41
+ `toLocaleString(undefined, { dateStyle: 'short', timeStyle: 'medium' })`
42
+ and prefixed with a "Purchased:" label.
43
+ 6. **"Buy Again" button** — opens the standard buy modal for the
44
+ same item. Disabled when:
45
+ - `can_buy === false` (server says the user cannot purchase it
46
+ right now), OR
47
+ - the user's current balance is below the item's CURRENT
48
+ `price` (not `purchase_points_amount`), OR
49
+ - `pool === 0` (out of stock).
50
+ Layout: inline on desktop (right column), in a bottom footer strip
51
+ on mobile.
52
+
53
+ ## Fields explicitly NOT rendered on the row
54
+ - `description`, `ribbon`, `hint_text`, `custom_data`, `type`,
55
+ `related_games`, `category_ids`, `discounted_price`,
56
+ `discount_price_ribbon`, `custom_ribbon_image`, `active_till_date`,
57
+ `cant_buy_message` — the default Smartico UI omits these on the history
58
+ row.
59
+
60
+ ## Detail view
61
+ No tap-to-expand inline detail view exists for history rows. "Buy
62
+ Again" is the only action; it routes through the standard
63
+ [getStoreItems](../../api/classes/WSAPIStore.md#getstoreitems) buy modal (full description, related games, etc.
64
+ are surfaced there).
65
+
66
+ ## Status-specific treatments
67
+ - No fulfillment status field; rows have no delivered/pending/cancelled
68
+ visual variants.
69
+ - Stock-based modifier: when `pool === 0`, grey out / disable the "Buy
70
+ Again" button and show the "Out of stock" label in the info area.
71
+ This is a CURRENT availability signal, not a status on the past
72
+ purchase itself.
73
+
74
+ ## Image / asset specs
75
+ - `image`: documented source size **256×256 px**, 1:1 aspect ratio.
76
+ - Typically rendered via `<img>` (the default Smartico UI uses an `<img>`,
77
+ not CSS background-image, for history rows).
78
+ - No built-in fallback placeholder — provide one when `image` is empty.
79
+
80
+ ## Date formatting
81
+ - Format `purchase_ts` with `toLocaleString(undefined, ...)` to render
82
+ in the user's OS locale and timezone. Do NOT assume UTC display — the
83
+ stored timestamp is UTC milliseconds but humans read it locally.
84
+ - For relative formatting ("3 hours ago", "Yesterday"), use
85
+ `purchased_today` / `purchased_this_week` / `purchased_this_month`
86
+ as buckets or compute deltas client-side.
87
+
88
+ ## Empty state
89
+ When the first page returns an empty array, render a "No purchases yet"
90
+ empty state (icon + copy). Do NOT distinguish "no purchases at all"
91
+ from "no purchases in selected period" — this method has no period
92
+ filter, so any empty response means the user has zero history entries
93
+ total.
94
+
95
+ ## Error handling
96
+ On a network or server failure the Promise rejects. The SDK does not
97
+ surface an `err_code`/`err_message` shape on this method (the array
98
+ either resolves or the call throws). Wrap in `try { ... } catch` and
99
+ either retry with backoff or surface a generic "Couldn't load purchase
100
+ history" message — the default Smartico UI's behavior is to silently clear
101
+ the loading state, which leaves the previous list visible.
102
+
103
+ ## Mobile vs desktop
104
+ - Desktop: two-column row (image left, info+actions right). "Buy Again"
105
+ and purchase-date inline in the info column.
106
+ - Mobile: single-column with a bottom footer strip carrying the
107
+ purchase-date and the "Buy Again" button.
108
+ - No CSS-breakpoint magic in the default Smartico UI — the layout toggle
109
+ is a runtime `isMobile` boolean.
110
+
111
+ ## Performance
112
+ - 20 rows per page is small enough that virtualization is not needed
113
+ under typical history sizes.
114
+ - Cache (30 s) is fine for in-screen re-renders. Do not call this on
115
+ every render — fetch once on screen mount and on demand for "Load
116
+ more" / `onUpdate` triggers.
117
+ - Polling is unnecessary — the `onUpdate` callback delivers fresh data
118
+ automatically when the user makes another purchase. For background
119
+ activity (purchases made on other surfaces), the cache TTL gives
120
+ eventual consistency within 30 seconds of the next call.
121
+
122
+ **Visitor mode**: not supported.
@@ -0,0 +1,140 @@
1
+ # UI Guide — `getClanTournamentPlayers`
2
+
3
+ ## Overview
4
+ - Powers the clan drill-down modal opened from a clan row on the
5
+ tournament detail's clan leaderboard tab (see
6
+ [`getTournamentInstanceInfo`](../../api/classes/WSAPITournaments.md#gettournamentinstanceinfo)).
7
+ - One-shot fetch. No cache, no polling, no subscription. Re-call to
8
+ refresh.
9
+ - Returns up to ~20 players, server-sorted by score DESC. Client-side
10
+ re-sort is unnecessary.
11
+
12
+ ## Modal / drawer layout
13
+
14
+ Top-to-bottom layout (centered modal on desktop, full-screen
15
+ slide-up sheet on mobile):
16
+
17
+ 1. **Close affordance** — explicit close button (top-right);
18
+ click-outside dismiss; Escape key dismiss.
19
+ 2. **Clan header** — clan image (1:1 aspect ratio, fallback to
20
+ first-letter avatar with the clan's name), clan name, total
21
+ score, contributing-members count, and a rank badge (1/2/3) if
22
+ applicable.
23
+
24
+ The default Smartico UI populates this header from the **caller-side**
25
+ `ClanLeaderboardEntry` object (the row the user clicked) rather
26
+ than from the response's `clan_public_meta` and `clan_id` — they
27
+ contain the same identity but the parent context already has
28
+ richer data (total_score, contributing_members, position) that the
29
+ response doesn't carry. The response's `clan_id` and
30
+ `clan_public_meta.name`/`image_url` are still available for
31
+ consumers that need them.
32
+
33
+ 3. **Table header row** — three columns: `#` (rank) · Player
34
+ (avatar + name) · Score.
35
+ 4. **Player list** — one row per `players[i]`. See "Player row".
36
+ 5. **Empty state** (when `players.length === 0`) — "No leaders" text.
37
+ Shown both during the loading round-trip AND when the result is
38
+ empty.
39
+
40
+ ## Player row
41
+
42
+ Three columns:
43
+
44
+ | Column | Source | Notes |
45
+ |---|---|---|
46
+ | `#` (rank) | `position ?? 0` | Plain text. Server-side rank within this clan. |
47
+ | Player (avatar + name) | `avatar_url` (or derived from `avatar_id` + label `avatarDomain`) + `clean_ext_user_id ?? public_username` | Username prefers `clean_ext_user_id` over `public_username`; if both empty, the avatar fallback letter is also blank. |
48
+ | Score | `scores ?? 0` | Format with space-thousands ("1 234 567") + "pts" suffix. |
49
+
50
+ **"Me" row highlight**: when `is_me === true`, add a distinct
51
+ background (CSS class `is-me` in the default Smartico UI). There is no
52
+ sticky "me" footer in this modal — the user's row scrolls with the
53
+ list. If your UI has many players (rare — server caps at ~20), you may
54
+ want to auto-scroll the "me" row into view on first render.
55
+
56
+ **Avatar resolution**: if `avatar_url` is already populated by the SDK,
57
+ use it directly. Otherwise, derive a CDN URL from `avatar_id` and the
58
+ brand's avatar domain — the SDK's `CoreUtils.avatarUrl()` does this for
59
+ you on the response.
60
+
61
+ **Top-3 styling**: optional — apply podium accent (gold/silver/bronze)
62
+ to rows where `position <= 3`. The default Smartico UI applies the
63
+ podium treatment only on the clan header rank badge, not on individual
64
+ player rows.
65
+
66
+ ## Empty / loading / error states
67
+
68
+ - **Loading**: render the modal frame and clan header immediately
69
+ (driven by caller-side data); render an "empty state" placeholder
70
+ in the player list area while the call is in flight. **Or** render a
71
+ skeleton rows (3–5 grey bars) — typically friendlier than the
72
+ empty-state text appearing then disappearing.
73
+ - **Empty (no players)**: render the "No leaders" text. This can mean:
74
+ - no one in the clan has scored yet,
75
+ - the call failed (network / server error — the default UI silently
76
+ swallows the error),
77
+ - the `(tournamentInstanceId, clanId)` combination is invalid.
78
+
79
+ These three are not distinguishable in the response itself. The
80
+ default UI treats them identically. If your UI cares to distinguish,
81
+ attach a `.catch()` to surface a transient-error toast.
82
+ - **Error**: not surfaced by default. Add explicit error handling if
83
+ needed — wrap the call in try/catch and show a "Failed to load — Try
84
+ again" affordance.
85
+
86
+ ## Close affordance
87
+
88
+ Three independent dismiss mechanisms (all dispatch the same close
89
+ action):
90
+
91
+ 1. Close button (top-right of modal).
92
+ 2. Click outside the modal card.
93
+ 3. `Escape` key.
94
+
95
+ The default Smartico UI wires all three via a shared `useModalClose`
96
+ hook. Mirror this pattern for parity.
97
+
98
+ ## Refresh
99
+
100
+ - **Don't poll inside this modal**. The default Smartico UI doesn't
101
+ poll — the clan-leaderboard tab (parent) does poll via
102
+ `getTournamentInstanceInfo` and refreshes the visible clan rankings
103
+ when the modal closes. Live updates inside the modal are not part
104
+ of the default UX.
105
+ - If your product requires live updates inside this modal, call
106
+ `getClanTournamentPlayers` again on a 3 s interval. Be cautious —
107
+ each call is a fresh network round-trip (no cache).
108
+
109
+ ## Pagination
110
+
111
+ None at the SDK level. The server returns up to ~20 players (configured
112
+ per tournament). There is no "load more" affordance in the default UI.
113
+
114
+ ## Animations / transitions
115
+
116
+ - **Modal entry**: fade-in + scale (~150 ms ease-out).
117
+ - **Modal exit**: fade-out + scale-down (~120 ms).
118
+ - **List entry**: cards/rows fade-in staggered ~20 ms per row on
119
+ first render. No skeleton-to-data crossfade (the list is small
120
+ enough to render instantly once data arrives).
121
+ - **Rank change between calls** (if you implement live polling): FLIP
122
+ animation to reorder rows.
123
+
124
+ ## Mobile vs desktop
125
+
126
+ - **Modal style**: centered overlay on desktop with backdrop dim;
127
+ full-screen slide-up sheet on mobile (with a swipe-down dismiss
128
+ gesture).
129
+ - **Header layout**: image left, identity right on desktop; stacked
130
+ (image above identity) on mobile.
131
+ - **Row density**: identical on both — the row is already compact.
132
+
133
+ ## Performance
134
+
135
+ - Each call is a fresh network round-trip. The page typically has at
136
+ most one of these in flight at a time (driven by user clicks),
137
+ so cost is minimal.
138
+ - If your UI offers a "view all clans" affordance that pre-fetches
139
+ player lists for many clans, throttle the fetches — don't fan out
140
+ 20 simultaneous round-trips.