@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.
Files changed (99) hide show
  1. package/.nvmrc +1 -1
  2. package/README.md +31 -2
  3. package/dist/Store/BuyStoreItemErrorCode.d.ts +9 -1
  4. package/dist/Tournaments/TournamentRegistrationError.d.ts +3 -1
  5. package/dist/WSAPI/WSAPI.d.ts +13 -1235
  6. package/dist/WSAPI/WSAPIAvatars.d.ts +78 -0
  7. package/dist/WSAPI/WSAPIBase.d.ts +71 -0
  8. package/dist/WSAPI/WSAPIBonuses.d.ts +26 -0
  9. package/dist/WSAPI/WSAPIClans.d.ts +388 -0
  10. package/dist/WSAPI/WSAPIGamePick.d.ts +308 -0
  11. package/dist/WSAPI/WSAPIGeneral.d.ts +30 -0
  12. package/dist/WSAPI/WSAPIInbox.d.ts +126 -0
  13. package/dist/WSAPI/WSAPIJackpots.d.ts +109 -0
  14. package/dist/WSAPI/WSAPILeaderBoard.d.ts +25 -0
  15. package/dist/WSAPI/WSAPIMiniGames.d.ts +94 -0
  16. package/dist/WSAPI/WSAPIMissions.d.ts +517 -0
  17. package/dist/WSAPI/WSAPIRaffles.d.ts +119 -0
  18. package/dist/WSAPI/WSAPIStore.d.ts +656 -0
  19. package/dist/WSAPI/WSAPITournaments.d.ts +501 -0
  20. package/dist/WSAPI/WSAPITypes.d.ts +145 -85
  21. package/dist/WSAPI/WSAPIUser.d.ts +383 -0
  22. package/dist/index.js +3818 -1528
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.modern.mjs +3180 -981
  25. package/dist/index.modern.mjs.map +1 -1
  26. package/docs/api/README.md +56 -2
  27. package/docs/api/classes/WSAPIAvatars.md +116 -0
  28. package/docs/api/classes/WSAPIBonuses.md +50 -0
  29. package/docs/api/classes/WSAPIClans.md +66 -0
  30. package/docs/api/classes/WSAPIGamePick.md +181 -0
  31. package/docs/api/classes/WSAPIGeneral.md +63 -0
  32. package/docs/api/classes/WSAPIInbox.md +277 -0
  33. package/docs/api/classes/WSAPIJackpots.md +185 -0
  34. package/docs/api/classes/WSAPILeaderBoard.md +39 -0
  35. package/docs/api/classes/WSAPIMiniGames.md +175 -0
  36. package/docs/api/classes/WSAPIMissions.md +611 -0
  37. package/docs/api/classes/WSAPIRaffles.md +195 -0
  38. package/docs/api/classes/WSAPIStore.md +744 -0
  39. package/docs/api/classes/WSAPITournaments.md +104 -0
  40. package/docs/api/classes/WSAPIUser.md +200 -0
  41. package/docs/api/enumerations/AchievementClaimPeriodTypeId.md +13 -0
  42. package/docs/api/enumerations/BuyStoreItemErrorCode.md +32 -0
  43. package/docs/api/interfaces/ClanPrizePublicMeta.md +41 -0
  44. package/docs/api/interfaces/ClanPrizeStructureEntry.md +7 -1
  45. package/docs/api/interfaces/ClanPublicMeta.md +4 -0
  46. package/docs/api/interfaces/SAWTemplateUI.md +10 -0
  47. package/docs/api/interfaces/TAchCategory.md +19 -4
  48. package/docs/api/interfaces/TBuyStoreItemResult.md +26 -8
  49. package/docs/api/interfaces/TMissionClaimRewardResult.md +14 -3
  50. package/docs/api/interfaces/TMissionOptInResult.md +10 -3
  51. package/docs/api/interfaces/TMissionOrBadge.md +6 -2
  52. package/docs/api/interfaces/TStoreItem.md +21 -1
  53. package/docs/api/interfaces/TTournamentDetailed.md +8 -0
  54. package/docs/api/interfaces/UserAchievement.md +1 -1
  55. package/docs/ui/clans/UIGuide_getClanInfo.md +153 -0
  56. package/docs/ui/clans/UIGuide_getClans.md +172 -0
  57. package/docs/ui/clans/UIGuide_joinClan.md +196 -0
  58. package/docs/ui/missions/UIGuide_getAchCategories.md +17 -0
  59. package/docs/ui/missions/UIGuide_getBadges.md +237 -0
  60. package/docs/ui/missions/UIGuide_getMissions.md +268 -0
  61. package/docs/ui/missions/UIGuide_requestMissionClaimReward.md +21 -0
  62. package/docs/ui/missions/UIGuide_requestMissionOptIn.md +16 -0
  63. package/docs/ui/store/UIGuide_buyStoreItem.md +133 -0
  64. package/docs/ui/store/UIGuide_getStoreCategories.md +19 -0
  65. package/docs/ui/store/UIGuide_getStoreItems.md +264 -0
  66. package/docs/ui/store/UIGuide_getStorePurchasedItems.md +122 -0
  67. package/docs/ui/tournaments/UIGuide_getClanTournamentPlayers.md +140 -0
  68. package/docs/ui/tournaments/UIGuide_getTournamentInstanceInfo.md +265 -0
  69. package/docs/ui/tournaments/UIGuide_getTournamentsList.md +236 -0
  70. package/docs/ui/tournaments/UIGuide_registerInTournament.md +156 -0
  71. package/docs/ui/user/UIGuide_getLevels.md +162 -0
  72. package/docs/ui/user/UIGuide_getUserProfile.md +213 -0
  73. package/package.json +3 -3
  74. package/scripts/postdoc-cleanup.js +195 -0
  75. package/src/Missions/MissionsUtils.ts +5 -2
  76. package/src/Store/BuyStoreItemErrorCode.ts +8 -0
  77. package/src/Store/StoreItem.ts +1 -0
  78. package/src/Store/StoreItemPurchased.ts +1 -0
  79. package/src/Store/StoreItemType.ts +1 -1
  80. package/src/Tournaments/TournamentRegistrationError.ts +4 -0
  81. package/src/WSAPI/WSAPI.ts +27 -2007
  82. package/src/WSAPI/WSAPIAvatars.ts +127 -0
  83. package/src/WSAPI/WSAPIBase.ts +93 -0
  84. package/src/WSAPI/WSAPIBonuses.ts +54 -0
  85. package/src/WSAPI/WSAPIClans.ts +417 -0
  86. package/src/WSAPI/WSAPIGamePick.ts +382 -0
  87. package/src/WSAPI/WSAPIGeneral.ts +57 -0
  88. package/src/WSAPI/WSAPIInbox.ts +222 -0
  89. package/src/WSAPI/WSAPIJackpots.ts +212 -0
  90. package/src/WSAPI/WSAPILeaderBoard.ts +40 -0
  91. package/src/WSAPI/WSAPIMiniGames.ts +181 -0
  92. package/src/WSAPI/WSAPIMissions.ts +572 -0
  93. package/src/WSAPI/WSAPIRaffles.ts +175 -0
  94. package/src/WSAPI/WSAPIStore.ts +732 -0
  95. package/src/WSAPI/WSAPITournaments.ts +541 -0
  96. package/src/WSAPI/WSAPITypes.ts +145 -87
  97. package/src/WSAPI/WSAPIUser.ts +456 -0
  98. package/tsconfig.json +17 -1
  99. package/docs/api/classes/WSAPI.md +0 -1927
@@ -214,4 +214,4 @@
214
214
 
215
215
  ### prize\_claim\_period\_type\_id?
216
216
 
217
- > `optional` **prize\_claim\_period\_type\_id?**: `AchievementClaimPeriodTypeId`
217
+ > `optional` **prize\_claim\_period\_type\_id?**: [`AchievementClaimPeriodTypeId`](../enumerations/AchievementClaimPeriodTypeId.md)
@@ -0,0 +1,153 @@
1
+ # UI Guide — `getClanInfo`
2
+
3
+ ## Overview
4
+ - Powers the **clan detail modal / page** — the surface opened after a
5
+ user clicks a row in the clan list (see
6
+ [`getClans`](../../api/classes/WSAPIClans.md#getclans)).
7
+ - One-shot fetch. No cache, no subscription, no push refresh. Re-call
8
+ to refresh.
9
+ - Returns everything a `TClan` list entry has PLUS the ranked
10
+ `members[]` roster and a fresh `cooldown_until`.
11
+
12
+ ## Detail modal layout
13
+
14
+ Top-to-bottom (single responsive component — same layout on mobile
15
+ and desktop, just resized via the modal frame):
16
+
17
+ 1. **Header** — close affordance, clan icon (`public_meta.image_url`
18
+ fallback to first-letter avatar), clan name, description.
19
+ 2. **Metadata row** — entry fee (or "Free"),
20
+ `member_count / capacity_limit` text, rating position badge.
21
+ 3. **Members list** — see "Members list".
22
+ 4. **Sticky "my member" footer** — when `members.find(m => m.is_me)`
23
+ is non-null, pin a duplicate of the user's member row at the
24
+ bottom of the scroll area. No row-count threshold — fires
25
+ whenever the user is in the clan, regardless of total member
26
+ count.
27
+ 5. **Join CTA** — see "Action button decision matrix" below.
28
+
29
+ There is NO inline capacity progress bar in the detail modal (the
30
+ progress bar appears only on the list-page row, not in detail).
31
+
32
+ ## Members list
33
+
34
+ Sourced from `members[]`, server-ordered by `contribution_score` DESC
35
+ (i.e. `position` ASC). No client-side re-sort required.
36
+
37
+ Per-row columns:
38
+
39
+ | Column | Source | Notes |
40
+ |---|---|---|
41
+ | Position | `member.position` | Plain text — no podium / top-3 styling in the detail modal. (The list page does apply podium styling to clan rows.) |
42
+ | Avatar | `member.avatar_url` (or derived from `avatar_id` + brand avatar domain) | Square. Render as CSS `background-image` or `<img>`. Letter-avatar fallback if both are empty. |
43
+ | Username | `member.public_username` | The default Smartico UI uses `public_username`; some sibling surfaces (tournament clan drill-down) prefer `member.clean_ext_user_id` as primary with `public_username` as fallback. Pick whichever matches your product's identity model. |
44
+ | "You" badge | rendered when `member.is_me === true` | Small inline pill next to the username. |
45
+ | Contribution | `member.contribution_score` | Format with `toLocaleString()` (locale-aware thousand separators). |
46
+
47
+ **"Me" row highlight**: when `member.is_me === true`, apply a row CSS
48
+ class (e.g. `is-me`) — background tint + inline "You" badge.
49
+
50
+ ## Sticky "my member" footer
51
+
52
+ The default Smartico UI pins a duplicate of the user's member row at
53
+ the bottom of the scrollable members list whenever the user is in this
54
+ clan (i.e. `members.find(m => m.is_me)` returns a non-null value).
55
+ Same visual treatment as the inline row, just pinned to the modal's
56
+ footer area above the Join CTA.
57
+
58
+ Skip the sticky footer when the user is not a member of this clan.
59
+
60
+ ## Action button decision matrix
61
+
62
+ Same as the list-level CTA matrix — evaluate in priority order. The
63
+ detail modal has the most accurate state because `cooldown_until` here
64
+ is always fresh (vs the list's 30 s-cached value).
65
+
66
+ | State | Condition | Button label | Enabled? |
67
+ |---|---|---|---|
68
+ | **Your clan** | `detail.clan_id === user_clan_id` | "Your clan" | No-op |
69
+ | **Cooldown active** | `detail.cooldown_until !== null` | `"Cooldown until <local-date>"` | Disabled |
70
+ | **Clan full** | `member_count >= capacity_limit` | "Clan is full" | Disabled |
71
+ | **Insufficient balance** | `entry_fee_amount > 0` AND balance for `entry_fee_currency_type_id` < `entry_fee_amount` | `"Not enough {Points|Gems|Diamonds} (deficit)"` | Disabled |
72
+ | **Joinable — free** | `entry_fee_amount === 0` OR `entry_fee_currency_type_id === 3` | "Join free" | Enabled |
73
+ | **Joinable — paid** | default | `"Join ({amount} {Points|Gems|Diamonds})"` | Enabled |
74
+
75
+ Enabled Join click invokes
76
+ [`joinClan(clan_id)`](../../api/classes/WSAPIClans.md#joinclan); see
77
+ its own UI guide.
78
+
79
+ **Note on cooldown freshness**: the detail's `cooldown_until` will
80
+ reflect a just-expired cooldown 30 s sooner than the list's cached
81
+ value. If your UI cares to surface a "you're now eligible" hint
82
+ right when the cooldown ends, watch the detail's value rather than
83
+ the list's.
84
+
85
+ ## Image / asset specs
86
+
87
+ | Field | Aspect ratio | Used where | Fallback |
88
+ |---|---|---|---|
89
+ | `public_meta.image_url` | 1:1 (square) | Modal header icon | First character of `public_meta.name` rendered as a letter avatar |
90
+ | `members[i].avatar_url` (or derived) | 1:1 | Member row avatars | Letter-avatar with the username's first character |
91
+
92
+ CDN-resolved automatically by the SDK. The default Smartico UI
93
+ re-derives member avatars from `avatar_id` + brand `avatarDomain`
94
+ even though `avatar_url` is pre-populated — either works.
95
+
96
+ ## Empty / loading / error states
97
+
98
+ - **Loading**: render the modal frame and header immediately
99
+ (driven by the caller-side `TClan` from the list); leave the
100
+ members list area as a shimmer skeleton until the detail
101
+ response lands.
102
+ - **`members` empty**: render an empty-state placeholder (e.g.
103
+ "No members yet"). Rare — clans with `member_count === 0` are
104
+ unusual.
105
+ - **Invalid `clanId` / network error**: the default Smartico UI
106
+ silently swallows the error and leaves the members list empty.
107
+ For better UX, attach a `.catch()` and surface "Failed to load
108
+ details — Try again".
109
+
110
+ ## Close affordance
111
+
112
+ Three independent dismiss mechanisms:
113
+
114
+ 1. Close button (top-right).
115
+ 2. Click outside the modal card.
116
+ 3. `Escape` key.
117
+
118
+ The default Smartico UI wires all three via a shared `useModalClose`
119
+ hook.
120
+
121
+ ## Animations / transitions
122
+
123
+ - **Modal entry**: fade-in + scale-up (~150 ms ease-out).
124
+ - **Modal exit**: fade-out + scale-down (~120 ms).
125
+ - **Member rows**: stagger fade-in (~20 ms per row) on first render.
126
+ - **Join success celebration**: when the user successfully joins
127
+ THIS clan (detect by `joinClan` resolving with `errCode === 0`
128
+ for `detail.clan_id`), re-fetch `getClanInfo` and animate the
129
+ sticky-footer row sliding in.
130
+ - **Rank changes between fetches**: if you poll the detail (uncommon
131
+ — typically only the list polls), use a FLIP animation when
132
+ `position` values shift between snapshots.
133
+
134
+ ## Mobile vs desktop
135
+
136
+ - **Single component** — no separate mobile / desktop variants of
137
+ the detail modal. Layout adapts via the modal frame's responsive
138
+ sizing.
139
+ - **Members list density**: identical on both. Rows are compact
140
+ enough to fit ~6–8 visible at once on a mobile sheet.
141
+ - **Sticky footer**: same logic on both — no row-count threshold
142
+ (different from the list-page sticky footer which requires ≥ 7
143
+ rows).
144
+
145
+ ## Performance
146
+
147
+ - Each call is a fresh network round-trip. Avoid auto-polling the
148
+ detail unless your UI explicitly needs live updates — the
149
+ default Smartico UI fetches once per modal mount.
150
+ - If you do poll for live rank/score updates, use a 10–30 s
151
+ interval and diff snapshots to animate only the changed rows.
152
+ - Member avatars use CDN-resized URLs; lazy-loading is usually
153
+ unnecessary for typical clan sizes (capacity_limit is the bound).
@@ -0,0 +1,172 @@
1
+ # UI Guide — `getClans`
2
+
3
+ ## Overview
4
+ - Powers the **clan-browser screen** (and the clan-pick modal during
5
+ tournament registration via
6
+ [`registerInTournament`](../../api/classes/WSAPITournaments.md#registerintournament)).
7
+ - Returned `clans[]` is already server-sorted by `rating_position` ASC
8
+ (1 = best). The default Smartico UI re-sorts on the same key as a
9
+ defensive measure but no client-side ordering work is required.
10
+ - Loading indicator: the first call is a server round-trip
11
+ (typically 100–500 ms); subsequent calls within the 30 s cache
12
+ window resolve effectively synchronously.
13
+ - `onUpdate` is poll-driven (no server push). For near-live state, the
14
+ consumer must poll on an interval — the default Smartico UI polls
15
+ every 30 s while the clan screen is open.
16
+
17
+ ## List view organization
18
+
19
+ A single flat ranked list — no tabs. Render `clans[]` in the order
20
+ returned (already sorted by `rating_position`).
21
+
22
+ **Client-side search filter**: filter `clan.public_meta.name` against
23
+ the user's typed query (case-insensitive). No server round-trip.
24
+
25
+ **Sticky "my clan" footer**: when the user is in a clan and the
26
+ visible list is long enough (the default Smartico UI uses ≥ 7 visible
27
+ items), pin a duplicate of the user's clan row at the bottom of the
28
+ scroll area so it remains visible while the user scrolls. Skip this
29
+ when the user is clanless or the list is short.
30
+
31
+ No dedicated "My Clan" panel at the top of the list — the user's clan
32
+ appears at its natural `rating_position`, just highlighted in place.
33
+
34
+ ## Item card / row
35
+
36
+ Fields rendered (column structure for desktop; mobile may collapse):
37
+
38
+ | Column | Source | Notes |
39
+ |---|---|---|
40
+ | Rank | `rating_position` | Display as `#N`. Apply `.top-1` / `.top-2` / `.top-3` styling for positions 1–3. |
41
+ | Icon / image | `public_meta.image_url` | Square. Fallback: first character of `public_meta.name` rendered as a letter avatar. |
42
+ | Name | `public_meta.name` | Translated server-side. Add "Your clan" badge next to the name if `clan.clan_id === user_clan_id`. |
43
+ | Rating score | `rating_score` | Numeric; the higher, the better. |
44
+ | Members | `member_count` / `capacity_limit` | Desktop: render a progress bar at `member_count / capacity_limit * 100%` with a label `N / M`. Mobile: text-only `N / M`. |
45
+ | Entry fee | `entry_fee_amount` + `entry_fee_currency_type_id` | "Free" when `entry_fee_amount === 0` or `entry_fee_currency_type_id === 3`. Otherwise `"{amount} {Points|Gems|Diamonds}"`. |
46
+
47
+ **Whole-row click target**: opens the detail modal (via
48
+ [`getClanInfo`](../../api/classes/WSAPIClans.md#getclaninfo) to load
49
+ members on demand). The row itself has no inline Join CTA — joining
50
+ happens from inside the detail modal (see "Action button decision
51
+ matrix" below).
52
+
53
+ **Detail-only fields** (not on row): `description`, full `members[]`
54
+ list (from `getClanInfo`), full progress bar styling on desktop.
55
+
56
+ ## Action button decision matrix
57
+
58
+ The Join CTA lives in the detail modal (or the clan-pick modal during
59
+ tournament registration). Evaluate in priority order — first match
60
+ wins.
61
+
62
+ | State | Condition | Button label | Enabled? | Notes |
63
+ |---|---|---|---|---|
64
+ | **Your clan** | `clan.clan_id === user_clan_id` | "Your clan" | No-op | Highlight; no join action. |
65
+ | **Cooldown active** | `cooldown_until !== null` | `"Cooldown until <local-date>"` | Disabled | Affects ALL clans — the cooldown is user-level, not per-clan. Format `cooldown_until` from UTC to local time (`DD-MM-YYYY HH:mm` style). |
66
+ | **Clan full** | `member_count >= capacity_limit` | "Clan is full" | Disabled | |
67
+ | **Insufficient balance** | `entry_fee_amount > 0` AND user's balance for `entry_fee_currency_type_id` < `entry_fee_amount` | `"Not enough {Points|Gems|Diamonds} (deficit)"` | Disabled | Cross-check against {@link getUserProfile} balance for the currency. |
68
+ | **Joinable — free** | `entry_fee_amount === 0` OR `entry_fee_currency_type_id === 3` | "Join free" | Enabled | |
69
+ | **Joinable — paid** | default | `"Join ({amount} {Points|Gems|Diamonds})"` | Enabled | |
70
+
71
+ The enabled Join click invokes
72
+ [`joinClan(clan_id)`](../../api/classes/WSAPIClans.md#joinclan). See
73
+ its own UI guide for the in-flight + error-code matrix.
74
+
75
+ **Picker mode (`registerInTournament` clan-pick modal)**: when the
76
+ user is in cooldown, the default Smartico UI disables every row in the
77
+ picker (not just the buttons) — `onClick` is suppressed entirely.
78
+
79
+ ## Cooldown rendering
80
+
81
+ - The cooldown is **user-level**, not per-clan. While
82
+ `cooldown_until !== null`, the user cannot join any clan.
83
+ - Format: `cooldown_until` is an ISO 8601 UTC string
84
+ (`"YYYY-MM-DDTHH:MM:SS"`, no timezone suffix). Parse as UTC, display
85
+ in user-local time. The default Smartico UI shows
86
+ `DD-MM-YYYY HH:mm` (e.g. `"01-06-2026 16:30"`).
87
+ - No "X days remaining" countdown — show the absolute expiry datetime.
88
+ If your product surface justifies a countdown, render one yourself.
89
+
90
+ ## Image / asset specs
91
+
92
+ | Field | Aspect ratio | Notes |
93
+ |---|---|---|
94
+ | `public_meta.image_url` | 1:1 (square) | Recommended source size not strictly enforced; the default Smartico UI sizes via CSS. Provide a square image for best results. |
95
+
96
+ **Fallback**: when `image_url` is missing or fails to load, render
97
+ the first character of `public_meta.name` as a letter avatar in the
98
+ icon container.
99
+
100
+ ## Status-specific visual treatments
101
+
102
+ - **Your clan**: row gets a `.highlight` background tint + "Your clan"
103
+ badge next to the name.
104
+ - **Top-3 rank**: rows for `rating_position` 1–3 get podium accents
105
+ (gold/silver/bronze).
106
+ - **Full clan**: no row-level treatment — only the detail-modal button
107
+ shows the disabled state.
108
+ - **Cooldown active**: only the detail-modal button is disabled (or
109
+ in the picker, the whole row is dimmed).
110
+ - **Insufficient balance**: only the detail-modal button is disabled.
111
+
112
+ ## Empty / loading / error states
113
+
114
+ - **Loading (first fetch)**: render a spinner / shimmer skeleton of
115
+ rows.
116
+ - **Empty list (no clans configured)**: render a neutral empty-state
117
+ illustration with copy like "No clans available".
118
+ - **Empty search results**: same empty-state illustration with
119
+ query-aware copy (e.g. "No clans matching '<query>'").
120
+ - **Error**: the default Smartico UI keeps the spinner visible
121
+ indefinitely on fetch failure (no error boundary). For a better UX,
122
+ catch the rejected promise and render a "Failed to load — Try again"
123
+ affordance.
124
+
125
+ ## Refresh
126
+
127
+ `onUpdate` is poll-driven only. Two ways to keep the list fresh:
128
+
129
+ 1. **Poll**: re-call `getClans()` on a 30-second interval while the
130
+ clan screen is visible. The cache deduplicates rapid refetches.
131
+ 2. **Re-call after mutations**: after a successful
132
+ [`joinClan`](../../api/classes/WSAPIClans.md#joinclan), re-call
133
+ `getClans()` immediately to pick up the new `user_clan_id` and any
134
+ refreshed `member_count` / `cooldown_until` fields.
135
+
136
+ There is no server push that refreshes the clans list automatically
137
+ — another user joining your clan, an operator kicking you, or a clan
138
+ being archived will all only surface on the next consumer-driven
139
+ fetch.
140
+
141
+ ## Animations / transitions
142
+
143
+ - **List entry**: rows fade-in with a ~30 ms stagger on first render.
144
+ - **Rank reorder between polls**: if you implement live updates,
145
+ detect `rating_position` changes by diffing the previous and
146
+ current arrays, and animate rows to their new position (FLIP
147
+ animation). Don't full-re-render the list.
148
+ - **Join success celebration**: when `user_clan_id` flips between
149
+ polls (detect by comparing pre- and post-refresh values), play a
150
+ brief "Welcome to {clan name}" toast and animate the now-highlighted
151
+ row.
152
+
153
+ ## Mobile vs desktop
154
+
155
+ - **Member fill**: desktop renders a progress bar; mobile renders
156
+ text-only (`N / M`).
157
+ - **Columns**: desktop shows `#`, Clan, Rating, Members, Entry;
158
+ mobile drops Members (entry fee inlines below the name).
159
+ - **Sticky my-clan footer**: same threshold on both (≥ 7 visible
160
+ rows).
161
+ - **Search**: same client-side filter; desktop typically has a
162
+ search input at the top; mobile may use a dedicated search sheet.
163
+
164
+ ## Performance
165
+
166
+ - 30 s polling on the clans surface is fine for typical clan
167
+ catalogs (< 200 clans). For very large catalogs, consider polling
168
+ only while the surface is in the viewport.
169
+ - Use keyed per-row rendering — celebration / rank-reorder animations
170
+ break if the DOM nodes are recreated on every poll tick.
171
+ - Image lazy-loading is safe; clans below the fold rarely need
172
+ immediate paint.
@@ -0,0 +1,196 @@
1
+ # UI Guide — `joinClan`
2
+
3
+ ## Overview
4
+ - Action-focused mutation. The user is on the clans browser or the
5
+ detail modal, the CTA is "Join (...)", and they click it.
6
+ - A successful response (`errCode === 0`) means: membership persisted,
7
+ entry fee (if any) already debited, cooldown restarted, CRM events
8
+ fired. The SDK has done all of this server-side by the time the
9
+ promise resolves.
10
+ - The SDK does NOT auto-refresh `getClans` after success. The
11
+ consumer must re-call manually.
12
+ - Latency: typically 200–800 ms for a healthy WebSocket connection
13
+ (the server performs balance checks + atomic DB writes
14
+ synchronously).
15
+
16
+ ## Loading state
17
+
18
+ - **Trigger**: set an in-flight flag (`joining = true`) immediately
19
+ on click, BEFORE the call. This is the consumer's responsibility
20
+ — the SDK has no in-flight guard.
21
+ - **Visual**: replace the button label with animated dots / spinner.
22
+ Disable the button. Keep the modal open.
23
+ - **Reset**: clear the in-flight flag on response — success or error.
24
+
25
+ ## Idempotency UI guard
26
+
27
+ **Two distinct hazards** the consumer must guard against:
28
+
29
+ 1. **Double-click during in-flight**: the SDK does NOT prevent two
30
+ simultaneous `joinClan` calls. If the user clicks twice before the
31
+ first response arrives, both requests reach the server. If the
32
+ first succeeds, the second behaves as a clan-switch to the same
33
+ clan — which, on labels with `CLAN_COOLDOWN_DAYS = 0`, will
34
+ **deduct the entry fee a second time**. Guard with an in-flight
35
+ flag (see "Loading state").
36
+
37
+ 2. **Same-clan re-click**: the SDK does NOT short-circuit when the
38
+ user clicks Join on the clan they're already in. The server
39
+ treats it as a clan-switch to the same clan, and on labels with
40
+ cooldown=0 will re-deduct the fee. The Join button MUST be
41
+ disabled (or show "Your clan") whenever
42
+ `target.clan_id === user_clan_id`. Don't rely on the server to
43
+ silently no-op.
44
+
45
+ Recommended pattern:
46
+
47
+ ```ts
48
+ const [joining, setJoining] = useState(false);
49
+
50
+ const onJoin = async () => {
51
+ if (joining) return; // in-flight guard
52
+ if (target.clan_id === userClanId) return; // same-clan guard
53
+ if (cooldownUntil) return; // cooldown guard
54
+ if (target.member_count >= target.capacity_limit) return;
55
+ // (also: balance guard — handled by the pre-render CTA state matrix)
56
+
57
+ setJoining(true);
58
+ const r = await window._smartico.api.joinClan(target.clan_id);
59
+ setJoining(false);
60
+ handleResponse(r);
61
+ if (r.errCode === 0) await window._smartico.api.getClans();
62
+ };
63
+ ```
64
+
65
+ ## Action button decision matrix
66
+
67
+ `errCode` branch behavior. Refer to the `joinClan` TSDoc for the full
68
+ table of codes; UI handling per code:
69
+
70
+ | `errCode` | Action |
71
+ |---|---|
72
+ | `0` (success) | Show success toast (e.g. `"Joined {clan.name}"`). Close modal (or transition it to the "Your clan" state). Call `getClans()` to refresh `user_clan_id`, `member_count`, and `cooldown_until`. The default Smartico UI also dispatches an optimistic local state update so the Join button instantly flips to "Your clan" before the refresh lands. |
73
+ | `1000` (invalid parameters) | Programming error from SDK consumer side — should not occur. Show a generic error toast. |
74
+ | `1001` (not found / archived) | Refresh `getClans()`; the clan should disappear from the list. Show a brief "clan no longer available" toast. |
75
+ | `1002` (full) | Show "Clan is full". Refresh `getClans()`; the now-full clan's button will pre-disable to "Clan is full". |
76
+ | `1003` (insufficient funds) | Show insufficient-balance UI naming the currency (`['points','gems','diamonds'][entry_fee_currency_type_id]`) and the deficit (`entry_fee_amount`). Optional: deep-link to a currency-earning surface. |
77
+ | `1004` (segment mismatch) | Show a gating message — operator-supplied copy if the clan exposes one; otherwise a generic "Not eligible for this clan". Don't retry. |
78
+ | `1005` | `joinClan` does NOT return `1005`. If you see it in this method's response, treat as a generic error. |
79
+ | `1006` (cooldown active) | Show "Cooldown until {cooldown_until local-formatted}". Disable the Join CTA across ALL clans until cooldown expires. |
80
+ | `1007` (archived) | Defined in the public enum but the server emits `1001` instead. Handle as a synonym for `1001` defensively. |
81
+ | `1011` | `joinClan` does NOT return `1011`. If you see it in this method's response, treat as a generic error. |
82
+ | Other | Generic error toast with `errMsg` if any. |
83
+
84
+ ## Per-error-code message strategy
85
+
86
+ | `errCode` | `errMsg` usable? | Operator-supplied copy | Best source |
87
+ |---|---|---|---|
88
+ | `0` (success) | — | — | Localized `"Joined {clan.name}"` |
89
+ | `1003` (insufficient funds) | Fallback | — | Localized `"Insufficient {currency}"` + deficit derived from `entry_fee_amount` and the user's balance |
90
+ | `1004` (segment mismatch) | Fallback | If the clan has an operator-supplied entry-conditions message, prefer it | Otherwise localized "Not eligible for this clan" |
91
+ | `1006` (cooldown) | Fallback | — | Localized "Cooldown until {date}" — format `cooldown_until` from UTC to local |
92
+ | `1002` (full) | Fallback | — | Localized "Clan is full" |
93
+ | All others | Use `errMsg` as a fallback when no specific localized copy is available | — | Localized error per code |
94
+
95
+ ## Optimistic update
96
+
97
+ The default Smartico UI optimistically flips the user's clan
98
+ membership state on `errCode === 0` (before `getClans()` refreshes).
99
+ This is **safe** for this method because:
100
+
101
+ - The server has already committed the membership row when the
102
+ response returns.
103
+ - The fee is already debited.
104
+ - If the optimistic update sets `user_clan_id` to the new clan, the
105
+ refresh from `getClans()` will confirm the same value.
106
+
107
+ You can mirror this pattern: on `errCode === 0`, immediately update
108
+ the UI to show the user in the new clan, then re-fetch in the
109
+ background. If the refresh disagrees (very unlikely), reconcile.
110
+
111
+ Do NOT optimistically update on any non-zero `errCode` — those are
112
+ real failures.
113
+
114
+ ## Success celebration
115
+
116
+ The default Smartico UI does not play a dedicated celebration
117
+ animation (no confetti, no full-screen overlay). The button flips
118
+ to "Your clan" immediately via optimistic update, and the modal
119
+ re-renders with refreshed member data once `getClans()` returns.
120
+
121
+ If your product surface justifies a celebration, drive it from the
122
+ `onUpdate` detection: when `result.user_clan_id` flips between
123
+ pre-join and post-join fetches, play your celebration.
124
+
125
+ ## Refresh after response
126
+
127
+ The SDK does NOT auto-refresh the `getClans` cache. After a
128
+ successful join, manually call `getClans()` to pick up:
129
+
130
+ - The new `user_clan_id`
131
+ - The new `cooldown_until` (cooldown restarts at join time)
132
+ - The updated `member_count` on the joined clan
133
+ - The decremented `member_count` on any previously-joined clan
134
+ (clan-switch case)
135
+
136
+ The user's balance update from the entry-fee debit arrives via the
137
+ SDK's user-properties update channel — subscribe separately to
138
+ {@link getUserProfile} if your UI shows the balance.
139
+
140
+ ## Clan-switch semantics
141
+
142
+ When the user is in clan A and successfully joins clan B, the
143
+ server performs an **atomic transition**: deactivate A membership,
144
+ insert B membership, debit fee — all in one transaction. The user
145
+ is never observably clanless during the switch.
146
+
147
+ From the UI's perspective, this is identical to a fresh join. No
148
+ special handling required — the response is the same `errCode === 0`,
149
+ and `getClans()` refresh will show the user in clan B with a fresh
150
+ cooldown.
151
+
152
+ ## Special case — tournament clan-pick flow
153
+
154
+ When `registerInTournament(tournamentId)` returns `1005` (user has
155
+ no clan and the tournament is clan-based), the default UI typically:
156
+
157
+ 1. Opens a clan-picker driven by `getClans`.
158
+ 2. User picks a clan → call `joinClan(clanId)`.
159
+ 3. On `errCode === 0`, automatically retry
160
+ `registerInTournament(tournamentId)`.
161
+ 4. Handle `1011` on the retry: user joined the clan but joined
162
+ after this tournament started (and has prior tournament scores)
163
+ — show an explanatory message; the user is in the clan but
164
+ blocked from this specific tournament run.
165
+
166
+ The SDK does NOT chain these calls automatically. Each step is the
167
+ consumer's responsibility. See the
168
+ [UI Guide for `registerInTournament`](../tournaments/UIGuide_registerInTournament.md)
169
+ for the recommended sequence.
170
+
171
+ ## Animations / transitions
172
+
173
+ - **Loading dots**: 3-dot bouncing animation on the button while the
174
+ promise is in flight.
175
+ - **Button state flip**: when the response arrives, animate the
176
+ button label transition (~150 ms ease-out).
177
+ - **Member list refresh**: after `getClans()` re-fetch, the affected
178
+ clan rows (`member_count` change, your clan now highlighted) get a
179
+ brief highlight pulse on update.
180
+
181
+ ## Mobile vs desktop
182
+
183
+ - **Loading state**: identical (dots on the button).
184
+ - **Error surfacing**: desktop typically uses a toast/snackbar;
185
+ mobile may use a centered modal with a clear "OK" affordance.
186
+ - **Cooldown / insufficient-balance disabled states**: rendered the
187
+ same way on both — the disabled button shows the constraint text.
188
+
189
+ ## Performance
190
+
191
+ - Single round-trip per click. No batching needed.
192
+ - The `getClans()` refresh after success adds ~50–500 ms (cache miss
193
+ → fresh fetch). Treat as fire-and-forget; the optimistic UI flip
194
+ has already happened.
195
+ - Don't poll `getClans()` to detect join completion — the response
196
+ promise is the authoritative signal.
@@ -0,0 +1,17 @@
1
+ # UI Guide — `getAchCategories`
2
+
3
+ ## UI guidance
4
+ - Treat categories as relatively static. It is safe to fetch once on page load 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
7
+ fall back to `name` or `id` for stable ordering.
8
+ - For tab UIs, generate one tab per category; "All" / "Other" tabs are
9
+ caller-provided (not returned by the server).
10
+ - For section UIs (mission/badge listing grouped by category), iterate sorted
11
+ categories and render only those whose filtered item list is non-empty unless
12
+ you intentionally want empty sections.
13
+ - Loading indicator: skip on cache hit; show a brief shimmer on cold fetch
14
+ (typically 100–300 ms).
15
+
16
+ **Visitor mode**: supported. Use `_smartico.vapi(lang).getAchCategories()` to fetch
17
+ categories with `name` translated to the given language without an authenticated user.