@taskon/widget-react 0.0.1
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/README.md +1064 -0
- package/dist/CommunityTaskList.css +5010 -0
- package/dist/EligibilityInfo.css +1966 -0
- package/dist/LeaderboardWidget.css +815 -0
- package/dist/Quest.css +4584 -0
- package/dist/Table.css +389 -0
- package/dist/TaskOnProvider.css +163 -0
- package/dist/WidgetShell.css +182 -0
- package/dist/chunks/CommunityTaskList-CrH6r4Av.js +6886 -0
- package/dist/chunks/EligibilityInfo-DesW9-k9.js +24900 -0
- package/dist/chunks/LeaderboardWidget-BSGpHKTk.js +1156 -0
- package/dist/chunks/Quest-uSIVq78I.js +8581 -0
- package/dist/chunks/Table-CWGf2FKV.js +449 -0
- package/dist/chunks/TaskOnProvider-QMwxGL44.js +1435 -0
- package/dist/chunks/ThemeProvider-Cs8IUVQj.js +1118 -0
- package/dist/chunks/WidgetShell-NlOgn1x5.js +1517 -0
- package/dist/chunks/common-ja-DWhTaFHb.js +23 -0
- package/dist/chunks/common-ko-80ezXsMG.js +23 -0
- package/dist/chunks/index-CwMvO_wZ.js +777 -0
- package/dist/chunks/leaderboardwidget-ja-Bj6gz6y1.js +119 -0
- package/dist/chunks/leaderboardwidget-ko-f1cLO9ic.js +119 -0
- package/dist/chunks/useToast-BGJhd3BX.js +93 -0
- package/dist/chunks/useWidgetLocale-BVcopbZS.js +74 -0
- package/dist/chunks/usercenter-ja-DBj_dtuz.js +329 -0
- package/dist/chunks/usercenter-ko-DYTkHAld.js +329 -0
- package/dist/community-task.d.ts +451 -0
- package/dist/community-task.js +9 -0
- package/dist/core.d.ts +803 -0
- package/dist/core.js +22 -0
- package/dist/index.css +3662 -0
- package/dist/index.d.ts +1627 -0
- package/dist/index.js +7372 -0
- package/dist/leaderboard.d.ts +547 -0
- package/dist/leaderboard.js +17 -0
- package/dist/quest.d.ts +389 -0
- package/dist/quest.js +8 -0
- package/package.json +97 -0
|
@@ -0,0 +1,1156 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useMemo, useCallback, useEffect } from "react";
|
|
3
|
+
import { createLeaderboardApi, LeaderboardContentType, formatRankRange, RewardsDistributeType, formatRewardText, calculatePrizePool, RewardType, LeaderboardTableColumn, isAllTimeResponse, isCampaignResponse } from "@taskon/core";
|
|
4
|
+
import { b as useTaskOnContext } from "./ThemeProvider-Cs8IUVQj.js";
|
|
5
|
+
import { D as Dialog, u as useResolvedWidgetConfig, W as WidgetShell } from "./WidgetShell-NlOgn1x5.js";
|
|
6
|
+
import { u as useWidgetLocale } from "./useWidgetLocale-BVcopbZS.js";
|
|
7
|
+
import { B as Button, T as Table, P as Pagination } from "./Table-CWGf2FKV.js";
|
|
8
|
+
import '../LeaderboardWidget.css';function usePagination(options) {
|
|
9
|
+
const { total, pageSize, initialPage = 0, mode = "pagination" } = options;
|
|
10
|
+
const [page2, setPage] = useState(initialPage);
|
|
11
|
+
const totalPages = useMemo(() => {
|
|
12
|
+
return Math.max(1, Math.ceil(total / pageSize));
|
|
13
|
+
}, [total, pageSize]);
|
|
14
|
+
const safePage = useMemo(() => {
|
|
15
|
+
return Math.min(page2, totalPages - 1);
|
|
16
|
+
}, [page2, totalPages]);
|
|
17
|
+
if (safePage !== page2) {
|
|
18
|
+
setPage(safePage);
|
|
19
|
+
}
|
|
20
|
+
const hasPrevious = safePage > 0;
|
|
21
|
+
const hasNext = safePage < totalPages - 1;
|
|
22
|
+
const goToPage = useCallback(
|
|
23
|
+
(targetPage) => {
|
|
24
|
+
const validPage = Math.max(0, Math.min(targetPage, totalPages - 1));
|
|
25
|
+
setPage(validPage);
|
|
26
|
+
},
|
|
27
|
+
[totalPages]
|
|
28
|
+
);
|
|
29
|
+
const goToPrevious = useCallback(() => {
|
|
30
|
+
if (hasPrevious) {
|
|
31
|
+
setPage((p) => p - 1);
|
|
32
|
+
}
|
|
33
|
+
}, [hasPrevious]);
|
|
34
|
+
const goToNext = useCallback(() => {
|
|
35
|
+
if (hasNext) {
|
|
36
|
+
setPage((p) => p + 1);
|
|
37
|
+
}
|
|
38
|
+
}, [hasNext]);
|
|
39
|
+
const reset = useCallback(() => {
|
|
40
|
+
setPage(0);
|
|
41
|
+
}, []);
|
|
42
|
+
const startIndex = safePage * pageSize + 1;
|
|
43
|
+
const endIndex = Math.min((safePage + 1) * pageSize, total);
|
|
44
|
+
const hasMore = useMemo(() => {
|
|
45
|
+
if (mode === "infinite") {
|
|
46
|
+
return safePage < totalPages - 1;
|
|
47
|
+
}
|
|
48
|
+
return hasNext;
|
|
49
|
+
}, [mode, safePage, totalPages, hasNext]);
|
|
50
|
+
const loadedCount = useMemo(() => {
|
|
51
|
+
if (mode === "infinite") {
|
|
52
|
+
return Math.min((safePage + 1) * pageSize, total);
|
|
53
|
+
}
|
|
54
|
+
return endIndex - startIndex + 1;
|
|
55
|
+
}, [mode, safePage, pageSize, total, startIndex, endIndex]);
|
|
56
|
+
const loadMore = useCallback(() => {
|
|
57
|
+
if (mode === "infinite" && hasMore) {
|
|
58
|
+
setPage((p) => p + 1);
|
|
59
|
+
}
|
|
60
|
+
}, [mode, hasMore]);
|
|
61
|
+
return {
|
|
62
|
+
page: safePage,
|
|
63
|
+
totalPages,
|
|
64
|
+
hasPrevious,
|
|
65
|
+
hasNext,
|
|
66
|
+
goToPage,
|
|
67
|
+
goToPrevious,
|
|
68
|
+
goToNext,
|
|
69
|
+
reset,
|
|
70
|
+
startIndex,
|
|
71
|
+
endIndex,
|
|
72
|
+
// Infinite 模式专用
|
|
73
|
+
loadMore,
|
|
74
|
+
hasMore,
|
|
75
|
+
loadedCount
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
|
|
79
|
+
const v = glob[path];
|
|
80
|
+
if (v) {
|
|
81
|
+
return typeof v === "function" ? v() : Promise.resolve(v);
|
|
82
|
+
}
|
|
83
|
+
return new Promise((_, reject) => {
|
|
84
|
+
(typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
|
|
85
|
+
reject.bind(
|
|
86
|
+
null,
|
|
87
|
+
new Error(
|
|
88
|
+
"Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
function useLeaderboard(options) {
|
|
95
|
+
const { client } = useTaskOnContext();
|
|
96
|
+
const [data, setData] = useState(null);
|
|
97
|
+
const [loading2, setLoading] = useState(false);
|
|
98
|
+
const [error2, setError] = useState(null);
|
|
99
|
+
const { tabConfig, page: page2, pageSize } = options;
|
|
100
|
+
const fetchData = useCallback(async () => {
|
|
101
|
+
if (!client) return;
|
|
102
|
+
setLoading(true);
|
|
103
|
+
setError(null);
|
|
104
|
+
try {
|
|
105
|
+
const api = createLeaderboardApi(client);
|
|
106
|
+
const pageParams = { page_no: page2, size: pageSize };
|
|
107
|
+
let result;
|
|
108
|
+
if (tabConfig.type === LeaderboardContentType.AllTime) {
|
|
109
|
+
result = await api.getAllTimeLeaderboard({
|
|
110
|
+
page: pageParams,
|
|
111
|
+
points_id: tabConfig.relatedId
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
const isIncentive = tabConfig.type === LeaderboardContentType.LeaderboardSprint;
|
|
115
|
+
result = await api.getCampaignLeaderboard({
|
|
116
|
+
campaign_id: tabConfig.relatedId,
|
|
117
|
+
page: pageParams,
|
|
118
|
+
incentive_campaign: isIncentive
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
setData(result);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
setError(err instanceof Error ? err : new Error("Unknown error"));
|
|
124
|
+
} finally {
|
|
125
|
+
setLoading(false);
|
|
126
|
+
}
|
|
127
|
+
}, [client, tabConfig, page2, pageSize]);
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
fetchData();
|
|
130
|
+
}, [fetchData]);
|
|
131
|
+
const { total, totalPages } = useMemo(() => {
|
|
132
|
+
if (!data) return { total: 0, totalPages: 0 };
|
|
133
|
+
const totalCount = data.total;
|
|
134
|
+
return {
|
|
135
|
+
total: totalCount,
|
|
136
|
+
totalPages: Math.ceil(totalCount / pageSize)
|
|
137
|
+
};
|
|
138
|
+
}, [data, pageSize]);
|
|
139
|
+
return {
|
|
140
|
+
data,
|
|
141
|
+
loading: loading2,
|
|
142
|
+
error: error2,
|
|
143
|
+
total,
|
|
144
|
+
totalPages,
|
|
145
|
+
refresh: fetchData
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function useCampaignReward(campaignId) {
|
|
149
|
+
const { client } = useTaskOnContext();
|
|
150
|
+
const [campaignInfo, setCampaignInfo] = useState(null);
|
|
151
|
+
const [loading2, setLoading] = useState(false);
|
|
152
|
+
const [error2, setError] = useState(null);
|
|
153
|
+
const fetchCampaignInfo = async () => {
|
|
154
|
+
if (!campaignId || !client) return;
|
|
155
|
+
setLoading(true);
|
|
156
|
+
setError(null);
|
|
157
|
+
try {
|
|
158
|
+
const api = createLeaderboardApi(client);
|
|
159
|
+
const info = await api.getCampaignInfo(campaignId);
|
|
160
|
+
setCampaignInfo(info);
|
|
161
|
+
} catch (err) {
|
|
162
|
+
setError(err instanceof Error ? err : new Error("Unknown error"));
|
|
163
|
+
} finally {
|
|
164
|
+
setLoading(false);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
fetchCampaignInfo();
|
|
169
|
+
}, [campaignId, client]);
|
|
170
|
+
const rewardSummary = useMemo(() => {
|
|
171
|
+
if (!campaignInfo) return null;
|
|
172
|
+
let totalWinners2 = 0;
|
|
173
|
+
const tiers = [];
|
|
174
|
+
campaignInfo.winner_rewards.forEach((wr) => {
|
|
175
|
+
wr.winner_layer_rewards.forEach((wlr, index) => {
|
|
176
|
+
var _a;
|
|
177
|
+
totalWinners2 += wlr.max_winners;
|
|
178
|
+
const { text: rankRangeText, from: rankFrom, to: rankTo } = formatRankRange(wlr, index);
|
|
179
|
+
const distributeType = ((_a = wlr.rewards[0]) == null ? void 0 : _a.reward_distribute_type) || RewardsDistributeType.Equally;
|
|
180
|
+
const simpleReward = campaignInfo.winner_rewards_simple[index];
|
|
181
|
+
tiers.push({
|
|
182
|
+
tierNo: index + 1,
|
|
183
|
+
rankRangeText,
|
|
184
|
+
rankFrom,
|
|
185
|
+
rankTo,
|
|
186
|
+
winnerCount: wlr.max_winners,
|
|
187
|
+
rewardText: formatRewardText(simpleReward, distributeType),
|
|
188
|
+
rewardAmount: (simpleReward == null ? void 0 : simpleReward.reward_amount) || "",
|
|
189
|
+
rewardSymbol: (simpleReward == null ? void 0 : simpleReward.reward_symbol) || "",
|
|
190
|
+
rewardIcon: (simpleReward == null ? void 0 : simpleReward.reward_icon) || "",
|
|
191
|
+
chainLabel: (simpleReward == null ? void 0 : simpleReward.chain_label) || "",
|
|
192
|
+
distributeType
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
const prizePool2 = calculatePrizePool(campaignInfo.winner_rewards_simple);
|
|
197
|
+
return {
|
|
198
|
+
prizePool: prizePool2,
|
|
199
|
+
totalWinners: totalWinners2,
|
|
200
|
+
tiers,
|
|
201
|
+
startTime: campaignInfo.start_time,
|
|
202
|
+
endTime: campaignInfo.end_time,
|
|
203
|
+
pointsName: "",
|
|
204
|
+
// TODO: 根据 incentive_rank_by_points_id 获取积分名称
|
|
205
|
+
isEnded: campaignInfo.is_end
|
|
206
|
+
};
|
|
207
|
+
}, [campaignInfo]);
|
|
208
|
+
return {
|
|
209
|
+
campaignInfo,
|
|
210
|
+
rewardSummary,
|
|
211
|
+
loading: loading2,
|
|
212
|
+
error: error2,
|
|
213
|
+
refresh: fetchCampaignInfo
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function HexagonBadge({
|
|
217
|
+
rank,
|
|
218
|
+
colorClass
|
|
219
|
+
}) {
|
|
220
|
+
return /* @__PURE__ */ jsxs("div", { className: `taskon-leaderboard-rank taskon-leaderboard-rank--badge taskon-leaderboard-rank--${colorClass}`, children: [
|
|
221
|
+
/* @__PURE__ */ jsx(
|
|
222
|
+
"svg",
|
|
223
|
+
{
|
|
224
|
+
width: "27",
|
|
225
|
+
height: "27",
|
|
226
|
+
viewBox: "0 0 27 27",
|
|
227
|
+
fill: "none",
|
|
228
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
229
|
+
className: "taskon-leaderboard-rank__hexagon",
|
|
230
|
+
children: /* @__PURE__ */ jsx(
|
|
231
|
+
"path",
|
|
232
|
+
{
|
|
233
|
+
d: "M13.5 1L25 7.5V19.5L13.5 26L2 19.5V7.5L13.5 1Z",
|
|
234
|
+
className: "taskon-leaderboard-rank__hexagon-path"
|
|
235
|
+
}
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
),
|
|
239
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-leaderboard-rank__number-overlay", children: rank })
|
|
240
|
+
] });
|
|
241
|
+
}
|
|
242
|
+
function RankBadge({ rank, isAllTime = false, className = "" }) {
|
|
243
|
+
if (rank < 0) {
|
|
244
|
+
return /* @__PURE__ */ jsx("span", { className: `taskon-leaderboard-rank taskon-leaderboard-rank--na ${className}`, children: "N/A" });
|
|
245
|
+
}
|
|
246
|
+
if (rank === 0) {
|
|
247
|
+
return /* @__PURE__ */ jsx("span", { className: `taskon-leaderboard-rank taskon-leaderboard-rank--unranked ${className}`, children: "--" });
|
|
248
|
+
}
|
|
249
|
+
if (rank === 1) {
|
|
250
|
+
return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx(HexagonBadge, { rank: 1, colorClass: "gold" }) });
|
|
251
|
+
}
|
|
252
|
+
if (rank === 2) {
|
|
253
|
+
return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx(HexagonBadge, { rank: 2, colorClass: "silver" }) });
|
|
254
|
+
}
|
|
255
|
+
if (rank === 3) {
|
|
256
|
+
return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx(HexagonBadge, { rank: 3, colorClass: "bronze" }) });
|
|
257
|
+
}
|
|
258
|
+
return /* @__PURE__ */ jsx("span", { className: `taskon-leaderboard-rank taskon-leaderboard-rank--number ${className}`, children: rank });
|
|
259
|
+
}
|
|
260
|
+
function UserCell({
|
|
261
|
+
userId,
|
|
262
|
+
userName,
|
|
263
|
+
avatar,
|
|
264
|
+
isCurrentUser = false,
|
|
265
|
+
youLabel = "YOU",
|
|
266
|
+
onUserClick,
|
|
267
|
+
className = ""
|
|
268
|
+
}) {
|
|
269
|
+
const handleClick = () => {
|
|
270
|
+
if (onUserClick) {
|
|
271
|
+
onUserClick(userId, userName);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
const isClickable = !!onUserClick && !isCurrentUser;
|
|
275
|
+
if (isCurrentUser) {
|
|
276
|
+
return /* @__PURE__ */ jsx("div", { className: `taskon-leaderboard-user taskon-leaderboard-user--current ${className}`, children: /* @__PURE__ */ jsx("span", { className: "taskon-leaderboard-user__you-badge", children: youLabel }) });
|
|
277
|
+
}
|
|
278
|
+
return /* @__PURE__ */ jsx(
|
|
279
|
+
"div",
|
|
280
|
+
{
|
|
281
|
+
className: `taskon-leaderboard-user ${isClickable ? "taskon-leaderboard-user--clickable" : ""} ${className}`,
|
|
282
|
+
onClick: isClickable ? handleClick : void 0,
|
|
283
|
+
role: isClickable ? "button" : void 0,
|
|
284
|
+
tabIndex: isClickable ? 0 : void 0,
|
|
285
|
+
onKeyDown: isClickable ? (e) => {
|
|
286
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
287
|
+
handleClick();
|
|
288
|
+
}
|
|
289
|
+
} : void 0,
|
|
290
|
+
children: /* @__PURE__ */ jsx("span", { className: "taskon-leaderboard-user__name", children: userName })
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
function getRewardTypeLabel(rewardType, rewardPointsName, messages) {
|
|
295
|
+
if (!rewardType) return null;
|
|
296
|
+
if (rewardType === RewardType.BMintedNft || rewardType === RewardType.Cap || rewardType === RewardType.Nft) {
|
|
297
|
+
return "NFT:";
|
|
298
|
+
}
|
|
299
|
+
if (rewardType === RewardType.DiscordRole) {
|
|
300
|
+
return "Discord Role:";
|
|
301
|
+
}
|
|
302
|
+
if (rewardType === RewardType.Exp) {
|
|
303
|
+
return "EXP:";
|
|
304
|
+
}
|
|
305
|
+
if (rewardType === RewardType.GTCPoints || rewardType === RewardType.Points) {
|
|
306
|
+
return `${rewardPointsName || messages.columnPoints}:`;
|
|
307
|
+
}
|
|
308
|
+
if (rewardType === RewardType.Token) {
|
|
309
|
+
return "Token:";
|
|
310
|
+
}
|
|
311
|
+
if (rewardType === RewardType.Whitelist) {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
function getRewardValueLabel(rewardType, rewardAmount, rewardSymbol, chainLabel, isUsdtEqual, messages) {
|
|
317
|
+
if (!rewardType) {
|
|
318
|
+
return {
|
|
319
|
+
value: rewardAmount ? `${rewardAmount} ${rewardSymbol || ""}` : messages.noData,
|
|
320
|
+
showChain: !!chainLabel
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
if (rewardType === RewardType.Nft || rewardType === RewardType.BMintedNft || rewardType === RewardType.DiscordRole || rewardType === RewardType.Exp) {
|
|
324
|
+
return { value: rewardSymbol || "", showChain: false };
|
|
325
|
+
}
|
|
326
|
+
if (rewardType === RewardType.Cap) {
|
|
327
|
+
return { value: "CAP", showChain: false };
|
|
328
|
+
}
|
|
329
|
+
if (rewardType === RewardType.GTCPoints || rewardType === RewardType.Points) {
|
|
330
|
+
return { value: rewardAmount || "", showChain: false };
|
|
331
|
+
}
|
|
332
|
+
if (rewardType === RewardType.Token) {
|
|
333
|
+
if (isUsdtEqual) {
|
|
334
|
+
return { value: `${rewardAmount} USD in ${rewardSymbol}`, showChain: false };
|
|
335
|
+
}
|
|
336
|
+
return { value: `${rewardAmount} ${rewardSymbol}`, showChain: !!chainLabel };
|
|
337
|
+
}
|
|
338
|
+
if (rewardType === RewardType.Whitelist) {
|
|
339
|
+
return { value: messages.whitelist || "Whitelist", showChain: false };
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
value: rewardAmount ? `${rewardAmount} ${rewardSymbol || ""}` : messages.noData,
|
|
343
|
+
showChain: !!chainLabel
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
function RewardCell({
|
|
347
|
+
rewardAmount,
|
|
348
|
+
rewardSymbol,
|
|
349
|
+
chainLabel,
|
|
350
|
+
rewardType,
|
|
351
|
+
rewardPointsName,
|
|
352
|
+
isUsdtEqual,
|
|
353
|
+
messages,
|
|
354
|
+
isPotential = false,
|
|
355
|
+
className = ""
|
|
356
|
+
}) {
|
|
357
|
+
if (!rewardAmount && !rewardType) {
|
|
358
|
+
return /* @__PURE__ */ jsx("span", { className, children: messages.noData });
|
|
359
|
+
}
|
|
360
|
+
const typeLabel = getRewardTypeLabel(rewardType, rewardPointsName, messages);
|
|
361
|
+
const { value, showChain } = getRewardValueLabel(
|
|
362
|
+
rewardType,
|
|
363
|
+
rewardAmount,
|
|
364
|
+
rewardSymbol,
|
|
365
|
+
chainLabel,
|
|
366
|
+
isUsdtEqual,
|
|
367
|
+
messages
|
|
368
|
+
);
|
|
369
|
+
const potentialClass = isPotential ? "taskon-leaderboard-table__reward--potential" : "";
|
|
370
|
+
return /* @__PURE__ */ jsxs("span", { className: `taskon-leaderboard-table__reward ${potentialClass} ${className}`, children: [
|
|
371
|
+
typeLabel && /* @__PURE__ */ jsx("span", { className: "taskon-leaderboard-table__reward-type", children: typeLabel }),
|
|
372
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-leaderboard-table__reward-value", children: value }),
|
|
373
|
+
showChain && chainLabel && /* @__PURE__ */ jsxs("span", { className: "taskon-leaderboard-table__reward-chain", children: [
|
|
374
|
+
"(",
|
|
375
|
+
chainLabel,
|
|
376
|
+
")"
|
|
377
|
+
] })
|
|
378
|
+
] });
|
|
379
|
+
}
|
|
380
|
+
function LeaderboardTabs({
|
|
381
|
+
tabs,
|
|
382
|
+
activeTabId,
|
|
383
|
+
onTabChange,
|
|
384
|
+
className = ""
|
|
385
|
+
}) {
|
|
386
|
+
if (tabs.length <= 1) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
return /* @__PURE__ */ jsx("div", { className: `taskon-leaderboard-tabs ${className}`, role: "tablist", children: tabs.map((tab) => {
|
|
390
|
+
const isActive = tab.id === activeTabId;
|
|
391
|
+
return /* @__PURE__ */ jsx(
|
|
392
|
+
"button",
|
|
393
|
+
{
|
|
394
|
+
role: "tab",
|
|
395
|
+
"aria-selected": isActive,
|
|
396
|
+
"aria-controls": `leaderboard-panel-${tab.id}`,
|
|
397
|
+
className: `taskon-leaderboard-tabs__tab ${isActive ? "taskon-leaderboard-tabs__tab--active" : ""}`,
|
|
398
|
+
onClick: () => onTabChange(tab.id),
|
|
399
|
+
children: tab.title
|
|
400
|
+
},
|
|
401
|
+
tab.id
|
|
402
|
+
);
|
|
403
|
+
}) });
|
|
404
|
+
}
|
|
405
|
+
function formatTimeRange(startTime, endTime) {
|
|
406
|
+
if (!startTime || !endTime) return "";
|
|
407
|
+
const start = new Date(startTime);
|
|
408
|
+
const end = new Date(endTime);
|
|
409
|
+
const offset = -start.getTimezoneOffset();
|
|
410
|
+
const offsetHours = Math.floor(Math.abs(offset) / 60);
|
|
411
|
+
const offsetSign = offset >= 0 ? "+" : "-";
|
|
412
|
+
const timezone = `UTC${offsetSign}${offsetHours}`;
|
|
413
|
+
const startYear = start.getFullYear();
|
|
414
|
+
const startMonth = String(start.getMonth() + 1).padStart(2, "0");
|
|
415
|
+
const startDay = String(start.getDate()).padStart(2, "0");
|
|
416
|
+
const startHours = String(start.getHours()).padStart(2, "0");
|
|
417
|
+
const startMinutes = String(start.getMinutes()).padStart(2, "0");
|
|
418
|
+
const endYear = end.getFullYear();
|
|
419
|
+
const endMonth = String(end.getMonth() + 1).padStart(2, "0");
|
|
420
|
+
const endDay = String(end.getDate()).padStart(2, "0");
|
|
421
|
+
const endHours = String(end.getHours()).padStart(2, "0");
|
|
422
|
+
const endMinutes = String(end.getMinutes()).padStart(2, "0");
|
|
423
|
+
return `(${timezone}) ${startYear}-${startMonth}-${startDay} ${startHours}:${startMinutes} ~ ${endYear}-${endMonth}-${endDay} ${endHours}:${endMinutes}`;
|
|
424
|
+
}
|
|
425
|
+
function getEventStatus(startTime, endTime, messages) {
|
|
426
|
+
if (!startTime || !endTime) return null;
|
|
427
|
+
const now = Date.now();
|
|
428
|
+
if (now < startTime) {
|
|
429
|
+
return { label: messages.statusUpcoming, status: "upcoming" };
|
|
430
|
+
}
|
|
431
|
+
if (now > endTime) {
|
|
432
|
+
return { label: messages.statusEnded, status: "ended" };
|
|
433
|
+
}
|
|
434
|
+
return { label: messages.statusOngoing, status: "ongoing" };
|
|
435
|
+
}
|
|
436
|
+
function LeaderboardHeader({
|
|
437
|
+
tabConfig,
|
|
438
|
+
showTitle,
|
|
439
|
+
rewardSummary,
|
|
440
|
+
participantsCount,
|
|
441
|
+
messages,
|
|
442
|
+
onOpenRewardRules,
|
|
443
|
+
className = ""
|
|
444
|
+
}) {
|
|
445
|
+
const isSprint = tabConfig.type === LeaderboardContentType.LeaderboardSprint;
|
|
446
|
+
const eventStatus = getEventStatus(tabConfig.startTime, tabConfig.endTime, messages);
|
|
447
|
+
const hasTimeRange = tabConfig.startTime && tabConfig.endTime;
|
|
448
|
+
const hasParticipants = typeof participantsCount === "number";
|
|
449
|
+
const hasContent = eventStatus || hasTimeRange || hasParticipants;
|
|
450
|
+
if (!hasContent && !showTitle && !isSprint) {
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
return /* @__PURE__ */ jsxs("div", { className: `taskon-leaderboard-header ${className}`, children: [
|
|
454
|
+
(eventStatus || hasTimeRange || hasParticipants) && /* @__PURE__ */ jsxs("div", { className: "taskon-leaderboard-header__info", children: [
|
|
455
|
+
eventStatus && /* @__PURE__ */ jsx(
|
|
456
|
+
"span",
|
|
457
|
+
{
|
|
458
|
+
className: `taskon-leaderboard-header__status taskon-leaderboard-header__status--${eventStatus.status}`,
|
|
459
|
+
children: eventStatus.label
|
|
460
|
+
}
|
|
461
|
+
),
|
|
462
|
+
hasTimeRange && /* @__PURE__ */ jsx("span", { className: "taskon-leaderboard-header__time", children: formatTimeRange(tabConfig.startTime, tabConfig.endTime) }),
|
|
463
|
+
hasParticipants && (eventStatus || hasTimeRange) && /* @__PURE__ */ jsx("div", { className: "taskon-leaderboard-header__separator" }),
|
|
464
|
+
hasParticipants && /* @__PURE__ */ jsxs("div", { className: "taskon-leaderboard-header__participants", children: [
|
|
465
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-leaderboard-header__participants-label", children: messages.participants || "Participants" }),
|
|
466
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-leaderboard-header__participants-value", children: participantsCount.toLocaleString() })
|
|
467
|
+
] })
|
|
468
|
+
] }),
|
|
469
|
+
isSprint && rewardSummary && /* @__PURE__ */ jsxs("div", { className: "taskon-leaderboard-header__reward-info", children: [
|
|
470
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-leaderboard-header__reward-item", children: [
|
|
471
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-leaderboard-header__reward-label", children: messages.prizePool }),
|
|
472
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-leaderboard-header__reward-value", children: rewardSummary.prizePool || "-" })
|
|
473
|
+
] }),
|
|
474
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-leaderboard-header__reward-separator" }),
|
|
475
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-leaderboard-header__reward-item", children: [
|
|
476
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-leaderboard-header__reward-label", children: messages.totalWinners }),
|
|
477
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-leaderboard-header__reward-value", children: rewardSummary.totalWinners })
|
|
478
|
+
] }),
|
|
479
|
+
onOpenRewardRules && /* @__PURE__ */ jsx(
|
|
480
|
+
Button,
|
|
481
|
+
{
|
|
482
|
+
variant: "ghost",
|
|
483
|
+
size: "small",
|
|
484
|
+
onClick: onOpenRewardRules,
|
|
485
|
+
className: "taskon-leaderboard-header__reward-rules-btn",
|
|
486
|
+
children: messages.rewardRules
|
|
487
|
+
}
|
|
488
|
+
)
|
|
489
|
+
] })
|
|
490
|
+
] });
|
|
491
|
+
}
|
|
492
|
+
function getColumnTitle(column, messages, pointsName) {
|
|
493
|
+
switch (column) {
|
|
494
|
+
case LeaderboardTableColumn.Rank:
|
|
495
|
+
return messages.columnRank;
|
|
496
|
+
case LeaderboardTableColumn.User:
|
|
497
|
+
return messages.columnUser;
|
|
498
|
+
case LeaderboardTableColumn.Tasks:
|
|
499
|
+
return messages.columnTasks;
|
|
500
|
+
case LeaderboardTableColumn.InvitedUsers:
|
|
501
|
+
return messages.columnInvitedUsers;
|
|
502
|
+
case LeaderboardTableColumn.CurrentTier:
|
|
503
|
+
return messages.columnCurrentTier;
|
|
504
|
+
case LeaderboardTableColumn.Points:
|
|
505
|
+
return pointsName || messages.columnPoints;
|
|
506
|
+
case LeaderboardTableColumn.Rewards:
|
|
507
|
+
return messages.columnRewards;
|
|
508
|
+
default:
|
|
509
|
+
return "";
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
function getRewardForRank(rank, rewardTiers) {
|
|
513
|
+
if (!rewardTiers || rank <= 0) return void 0;
|
|
514
|
+
return rewardTiers.find((tier) => {
|
|
515
|
+
return rank >= tier.rankFrom && rank <= tier.rankTo;
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
function convertAllTimeData(data) {
|
|
519
|
+
return data.map((item) => ({
|
|
520
|
+
id: item.user_id,
|
|
521
|
+
rank: item.rank,
|
|
522
|
+
displayRank: item.rank,
|
|
523
|
+
userId: item.user_id,
|
|
524
|
+
userName: item.user,
|
|
525
|
+
avatar: item.avatar,
|
|
526
|
+
tasks: item.task,
|
|
527
|
+
invitedUsers: item.invited_users,
|
|
528
|
+
points: item.point.amount,
|
|
529
|
+
isAllTime: true
|
|
530
|
+
}));
|
|
531
|
+
}
|
|
532
|
+
function convertCampaignData(data) {
|
|
533
|
+
return data.map((item) => {
|
|
534
|
+
var _a;
|
|
535
|
+
return {
|
|
536
|
+
id: item.user_info.user_id,
|
|
537
|
+
rank: item.rank,
|
|
538
|
+
displayRank: item.rank >= 0 ? item.rank + 1 : item.rank,
|
|
539
|
+
// Campaign 的 rank 从 0 开始
|
|
540
|
+
userId: item.user_info.user_id,
|
|
541
|
+
userName: item.user_info.user_name,
|
|
542
|
+
avatar: item.user_info.user_avatar,
|
|
543
|
+
tasks: item.task_count,
|
|
544
|
+
currentTier: item.winner_layer !== void 0 && item.winner_layer >= 0 ? item.winner_layer + 1 : void 0,
|
|
545
|
+
points: item.point.amount,
|
|
546
|
+
rewardAmount: item.reward_amount,
|
|
547
|
+
rewardSymbol: item.reward_symbol,
|
|
548
|
+
chainLabel: item.chain_label,
|
|
549
|
+
rewardType: item.reward_type,
|
|
550
|
+
rewardPointsName: (_a = item.point) == null ? void 0 : _a.points_name,
|
|
551
|
+
isUsdtEqual: item.is_usdt_equal,
|
|
552
|
+
isAllTime: false
|
|
553
|
+
};
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
function LeaderboardTable({
|
|
557
|
+
tabConfig,
|
|
558
|
+
allTimeData,
|
|
559
|
+
campaignData,
|
|
560
|
+
currentUserId,
|
|
561
|
+
messages,
|
|
562
|
+
onUserClick,
|
|
563
|
+
rewardTiers,
|
|
564
|
+
loading: loading2 = false,
|
|
565
|
+
className = ""
|
|
566
|
+
}) {
|
|
567
|
+
const isAllTime = tabConfig.type === LeaderboardContentType.AllTime;
|
|
568
|
+
const tableData = useMemo(() => {
|
|
569
|
+
if (isAllTime && allTimeData) {
|
|
570
|
+
return convertAllTimeData(allTimeData);
|
|
571
|
+
}
|
|
572
|
+
if (!isAllTime && campaignData) {
|
|
573
|
+
return convertCampaignData(campaignData);
|
|
574
|
+
}
|
|
575
|
+
return [];
|
|
576
|
+
}, [isAllTime, allTimeData, campaignData]);
|
|
577
|
+
const columns = useMemo(() => {
|
|
578
|
+
return tabConfig.columns.map((column) => {
|
|
579
|
+
const baseColumn = {
|
|
580
|
+
key: column,
|
|
581
|
+
title: getColumnTitle(column, messages, tabConfig.pointsName)
|
|
582
|
+
};
|
|
583
|
+
switch (column) {
|
|
584
|
+
case LeaderboardTableColumn.Rank:
|
|
585
|
+
return {
|
|
586
|
+
...baseColumn,
|
|
587
|
+
width: 120,
|
|
588
|
+
render: (_, row) => /* @__PURE__ */ jsx(RankBadge, { rank: row.displayRank, isAllTime: row.isAllTime })
|
|
589
|
+
};
|
|
590
|
+
case LeaderboardTableColumn.User:
|
|
591
|
+
return {
|
|
592
|
+
...baseColumn,
|
|
593
|
+
minWidth: 164,
|
|
594
|
+
render: (_, row) => /* @__PURE__ */ jsx(
|
|
595
|
+
UserCell,
|
|
596
|
+
{
|
|
597
|
+
userId: row.userId,
|
|
598
|
+
userName: row.userName,
|
|
599
|
+
avatar: row.avatar,
|
|
600
|
+
isCurrentUser: currentUserId === row.userId,
|
|
601
|
+
youLabel: messages.you,
|
|
602
|
+
onUserClick
|
|
603
|
+
}
|
|
604
|
+
)
|
|
605
|
+
};
|
|
606
|
+
case LeaderboardTableColumn.Tasks:
|
|
607
|
+
return {
|
|
608
|
+
...baseColumn,
|
|
609
|
+
width: 142,
|
|
610
|
+
render: (_, row) => row.tasks ?? messages.noData
|
|
611
|
+
};
|
|
612
|
+
case LeaderboardTableColumn.InvitedUsers:
|
|
613
|
+
return {
|
|
614
|
+
...baseColumn,
|
|
615
|
+
width: 142,
|
|
616
|
+
render: (_, row) => row.invitedUsers ?? messages.noData
|
|
617
|
+
};
|
|
618
|
+
case LeaderboardTableColumn.CurrentTier:
|
|
619
|
+
return {
|
|
620
|
+
...baseColumn,
|
|
621
|
+
width: 142,
|
|
622
|
+
render: (_, row) => row.currentTier ?? messages.noData
|
|
623
|
+
};
|
|
624
|
+
case LeaderboardTableColumn.Points:
|
|
625
|
+
return {
|
|
626
|
+
...baseColumn,
|
|
627
|
+
width: 142,
|
|
628
|
+
render: (_, row) => row.points && row.points > 0 ? row.points.toLocaleString() : messages.noData
|
|
629
|
+
};
|
|
630
|
+
case LeaderboardTableColumn.Rewards:
|
|
631
|
+
return {
|
|
632
|
+
...baseColumn,
|
|
633
|
+
width: 103,
|
|
634
|
+
align: "right",
|
|
635
|
+
render: (_, row) => {
|
|
636
|
+
if (row.rewardAmount || row.rewardType) {
|
|
637
|
+
return /* @__PURE__ */ jsx(
|
|
638
|
+
RewardCell,
|
|
639
|
+
{
|
|
640
|
+
rewardAmount: row.rewardAmount,
|
|
641
|
+
rewardSymbol: row.rewardSymbol,
|
|
642
|
+
chainLabel: row.chainLabel,
|
|
643
|
+
rewardType: row.rewardType,
|
|
644
|
+
rewardPointsName: row.rewardPointsName,
|
|
645
|
+
isUsdtEqual: row.isUsdtEqual,
|
|
646
|
+
messages
|
|
647
|
+
}
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
const reward = getRewardForRank(row.displayRank, rewardTiers);
|
|
651
|
+
if (reward) {
|
|
652
|
+
return /* @__PURE__ */ jsx(
|
|
653
|
+
RewardCell,
|
|
654
|
+
{
|
|
655
|
+
rewardAmount: reward.rewardAmount,
|
|
656
|
+
rewardSymbol: reward.rewardSymbol,
|
|
657
|
+
chainLabel: reward.chainLabel,
|
|
658
|
+
messages,
|
|
659
|
+
isPotential: true
|
|
660
|
+
}
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
return messages.noData;
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
default:
|
|
667
|
+
return {
|
|
668
|
+
...baseColumn,
|
|
669
|
+
render: () => messages.noData
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
}, [tabConfig.columns, tabConfig.pointsName, messages, currentUserId, onUserClick, rewardTiers]);
|
|
674
|
+
const rowConfig = useMemo(
|
|
675
|
+
() => ({
|
|
676
|
+
getRowKey: (row) => row.id,
|
|
677
|
+
isHighlighted: (row) => currentUserId === row.userId
|
|
678
|
+
}),
|
|
679
|
+
[currentUserId]
|
|
680
|
+
);
|
|
681
|
+
const emptyConfig = useMemo(
|
|
682
|
+
() => ({
|
|
683
|
+
title: messages.emptyTitle,
|
|
684
|
+
description: messages.emptyDesc
|
|
685
|
+
}),
|
|
686
|
+
[messages.emptyTitle, messages.emptyDesc]
|
|
687
|
+
);
|
|
688
|
+
return /* @__PURE__ */ jsx(
|
|
689
|
+
Table,
|
|
690
|
+
{
|
|
691
|
+
columns,
|
|
692
|
+
data: tableData,
|
|
693
|
+
rowConfig,
|
|
694
|
+
loading: loading2,
|
|
695
|
+
loadingText: messages.loading,
|
|
696
|
+
empty: emptyConfig,
|
|
697
|
+
striped: true,
|
|
698
|
+
className
|
|
699
|
+
}
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
function LeaderboardPagination({
|
|
703
|
+
page: page2,
|
|
704
|
+
totalPages,
|
|
705
|
+
total,
|
|
706
|
+
hasPrevious,
|
|
707
|
+
hasNext,
|
|
708
|
+
onPrevious,
|
|
709
|
+
onNext,
|
|
710
|
+
messages,
|
|
711
|
+
className = ""
|
|
712
|
+
}) {
|
|
713
|
+
if (totalPages <= 1 || total === 0) {
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
716
|
+
return /* @__PURE__ */ jsx(
|
|
717
|
+
Pagination,
|
|
718
|
+
{
|
|
719
|
+
page: page2,
|
|
720
|
+
totalPages,
|
|
721
|
+
hasPrevious,
|
|
722
|
+
hasNext,
|
|
723
|
+
onPrevious,
|
|
724
|
+
onNext,
|
|
725
|
+
className: `taskon-leaderboard-pagination ${className}`,
|
|
726
|
+
showArrows: true,
|
|
727
|
+
showButtonText: false
|
|
728
|
+
}
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
function formatDate(timestamp) {
|
|
732
|
+
if (!timestamp) return "";
|
|
733
|
+
const date = new Date(timestamp);
|
|
734
|
+
return date.toLocaleDateString("en-US", {
|
|
735
|
+
year: "numeric",
|
|
736
|
+
month: "short",
|
|
737
|
+
day: "numeric",
|
|
738
|
+
hour: "2-digit",
|
|
739
|
+
minute: "2-digit"
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
function getDistributeText(distributeType, messages) {
|
|
743
|
+
switch (distributeType) {
|
|
744
|
+
case RewardsDistributeType.Equally:
|
|
745
|
+
return "Each receives";
|
|
746
|
+
case RewardsDistributeType.Random:
|
|
747
|
+
return "Randomly split";
|
|
748
|
+
default:
|
|
749
|
+
return "";
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
function TierRow({
|
|
753
|
+
tier,
|
|
754
|
+
messages
|
|
755
|
+
}) {
|
|
756
|
+
const distributeText = getDistributeText(tier.distributeType);
|
|
757
|
+
return /* @__PURE__ */ jsxs("div", { className: "taskon-leaderboard-modal__tier", children: [
|
|
758
|
+
/* @__PURE__ */ jsxs("span", { className: "taskon-leaderboard-modal__tier-rank", children: [
|
|
759
|
+
"Top ",
|
|
760
|
+
tier.rankRangeText
|
|
761
|
+
] }),
|
|
762
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-leaderboard-modal__tier-reward", children: [
|
|
763
|
+
distributeText && /* @__PURE__ */ jsx("span", { className: "taskon-leaderboard-modal__tier-distribute", children: distributeText }),
|
|
764
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-leaderboard-modal__tier-token", children: [
|
|
765
|
+
tier.rewardIcon && /* @__PURE__ */ jsx(
|
|
766
|
+
"img",
|
|
767
|
+
{
|
|
768
|
+
src: tier.rewardIcon,
|
|
769
|
+
alt: tier.rewardSymbol,
|
|
770
|
+
className: "taskon-leaderboard-modal__tier-token-icon"
|
|
771
|
+
}
|
|
772
|
+
),
|
|
773
|
+
/* @__PURE__ */ jsxs("span", { className: "taskon-leaderboard-modal__tier-amount", children: [
|
|
774
|
+
/* @__PURE__ */ jsxs("span", { className: "taskon-leaderboard-modal__tier-amount-value", children: [
|
|
775
|
+
tier.rewardAmount,
|
|
776
|
+
" "
|
|
777
|
+
] }),
|
|
778
|
+
/* @__PURE__ */ jsx("span", { className: "taskon-leaderboard-modal__tier-amount-symbol", children: tier.rewardSymbol })
|
|
779
|
+
] })
|
|
780
|
+
] })
|
|
781
|
+
] })
|
|
782
|
+
] });
|
|
783
|
+
}
|
|
784
|
+
function RewardRulesModal({
|
|
785
|
+
isOpen,
|
|
786
|
+
onClose,
|
|
787
|
+
rewardSummary,
|
|
788
|
+
messages,
|
|
789
|
+
className = ""
|
|
790
|
+
}) {
|
|
791
|
+
if (!rewardSummary) {
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
const description = messages.rewardRulesDesc.replace(
|
|
795
|
+
"{pointsName}",
|
|
796
|
+
rewardSummary.pointsName || "points"
|
|
797
|
+
);
|
|
798
|
+
return /* @__PURE__ */ jsx(
|
|
799
|
+
Dialog,
|
|
800
|
+
{
|
|
801
|
+
open: isOpen,
|
|
802
|
+
onOpenChange: (open) => {
|
|
803
|
+
if (!open) onClose();
|
|
804
|
+
},
|
|
805
|
+
title: messages.rewardRulesTitle,
|
|
806
|
+
showCloseButton: true,
|
|
807
|
+
className,
|
|
808
|
+
contentClassName: "taskon-leaderboard-modal",
|
|
809
|
+
maxWidth: 480,
|
|
810
|
+
children: /* @__PURE__ */ jsxs("div", { className: "taskon-leaderboard-modal__content", children: [
|
|
811
|
+
/* @__PURE__ */ jsx("h2", { className: "taskon-leaderboard-modal__title", children: messages.rewardRulesTitle }),
|
|
812
|
+
/* @__PURE__ */ jsx("p", { className: "taskon-leaderboard-modal__desc", children: description }),
|
|
813
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-leaderboard-modal__section", children: [
|
|
814
|
+
/* @__PURE__ */ jsx("h3", { className: "taskon-leaderboard-modal__section-title", children: messages.eventPeriod }),
|
|
815
|
+
/* @__PURE__ */ jsxs("p", { className: "taskon-leaderboard-modal__period", children: [
|
|
816
|
+
formatDate(rewardSummary.startTime),
|
|
817
|
+
" -",
|
|
818
|
+
" ",
|
|
819
|
+
formatDate(rewardSummary.endTime)
|
|
820
|
+
] })
|
|
821
|
+
] }),
|
|
822
|
+
/* @__PURE__ */ jsxs("div", { className: "taskon-leaderboard-modal__section", children: [
|
|
823
|
+
/* @__PURE__ */ jsx("h3", { className: "taskon-leaderboard-modal__section-title", children: messages.rewardDetails }),
|
|
824
|
+
/* @__PURE__ */ jsx("div", { className: "taskon-leaderboard-modal__tiers", children: rewardSummary.tiers.map((tier) => /* @__PURE__ */ jsx(TierRow, { tier, messages }, tier.tierNo)) })
|
|
825
|
+
] })
|
|
826
|
+
] })
|
|
827
|
+
}
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
function mergeLeaderboardConfig(props, cloud) {
|
|
831
|
+
return {
|
|
832
|
+
config: props.config ?? cloud ?? null
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
const tabAllTime = "All Time";
|
|
836
|
+
const tabQuest = "Quest";
|
|
837
|
+
const tabEvent = "Event";
|
|
838
|
+
const tabSprint = "Sprint";
|
|
839
|
+
const columnRank = "Rank";
|
|
840
|
+
const columnPosition = "Position";
|
|
841
|
+
const columnUser = "User";
|
|
842
|
+
const columnTasks = "Tasks";
|
|
843
|
+
const columnInvitedUsers = "Invited Users";
|
|
844
|
+
const columnCurrentTier = "Current Tier";
|
|
845
|
+
const columnPoints = "Points";
|
|
846
|
+
const columnRewards = "Reward";
|
|
847
|
+
const statusOngoing = "Ongoing";
|
|
848
|
+
const statusEnded = "Ended";
|
|
849
|
+
const statusUpcoming = "Upcoming";
|
|
850
|
+
const participants = "Participants";
|
|
851
|
+
const prizePool = "Prize Pool";
|
|
852
|
+
const totalWinners = "Total Winners";
|
|
853
|
+
const rewardRules = "Reward Rules";
|
|
854
|
+
const rewardRulesTitle = "Reward Rules";
|
|
855
|
+
const rewardRulesDesc = "Rewards will be given based on leaderboard rankings, determined by users' {pointsName} earned during the event.";
|
|
856
|
+
const eventPeriod = "Event Period";
|
|
857
|
+
const rewardDetails = "Reward Details";
|
|
858
|
+
const eachReceives = "Each receives {amount}";
|
|
859
|
+
const randomlySplit = "Randomly split {amount}";
|
|
860
|
+
const emptyTitle = "No Rankings Yet";
|
|
861
|
+
const emptyDesc = "Be the first to join and claim the top spot!";
|
|
862
|
+
const page = "Page";
|
|
863
|
+
const of = "of";
|
|
864
|
+
const previous = "Previous";
|
|
865
|
+
const next = "Next";
|
|
866
|
+
const loading = "Loading...";
|
|
867
|
+
const error = "Failed to load leaderboard";
|
|
868
|
+
const retry = "Retry";
|
|
869
|
+
const you = "You";
|
|
870
|
+
const unranked = "Unranked";
|
|
871
|
+
const na = "N/A";
|
|
872
|
+
const noData = "-";
|
|
873
|
+
const whitelist = "Whitelist";
|
|
874
|
+
const enMessages = {
|
|
875
|
+
tabAllTime,
|
|
876
|
+
tabQuest,
|
|
877
|
+
tabEvent,
|
|
878
|
+
tabSprint,
|
|
879
|
+
columnRank,
|
|
880
|
+
columnPosition,
|
|
881
|
+
columnUser,
|
|
882
|
+
columnTasks,
|
|
883
|
+
columnInvitedUsers,
|
|
884
|
+
columnCurrentTier,
|
|
885
|
+
columnPoints,
|
|
886
|
+
columnRewards,
|
|
887
|
+
statusOngoing,
|
|
888
|
+
statusEnded,
|
|
889
|
+
statusUpcoming,
|
|
890
|
+
participants,
|
|
891
|
+
prizePool,
|
|
892
|
+
totalWinners,
|
|
893
|
+
rewardRules,
|
|
894
|
+
rewardRulesTitle,
|
|
895
|
+
rewardRulesDesc,
|
|
896
|
+
eventPeriod,
|
|
897
|
+
rewardDetails,
|
|
898
|
+
eachReceives,
|
|
899
|
+
randomlySplit,
|
|
900
|
+
emptyTitle,
|
|
901
|
+
emptyDesc,
|
|
902
|
+
page,
|
|
903
|
+
of,
|
|
904
|
+
previous,
|
|
905
|
+
next,
|
|
906
|
+
loading,
|
|
907
|
+
error,
|
|
908
|
+
retry,
|
|
909
|
+
you,
|
|
910
|
+
unranked,
|
|
911
|
+
na,
|
|
912
|
+
noData,
|
|
913
|
+
whitelist
|
|
914
|
+
};
|
|
915
|
+
const en = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
916
|
+
__proto__: null,
|
|
917
|
+
columnCurrentTier,
|
|
918
|
+
columnInvitedUsers,
|
|
919
|
+
columnPoints,
|
|
920
|
+
columnPosition,
|
|
921
|
+
columnRank,
|
|
922
|
+
columnRewards,
|
|
923
|
+
columnTasks,
|
|
924
|
+
columnUser,
|
|
925
|
+
default: enMessages,
|
|
926
|
+
eachReceives,
|
|
927
|
+
emptyDesc,
|
|
928
|
+
emptyTitle,
|
|
929
|
+
error,
|
|
930
|
+
eventPeriod,
|
|
931
|
+
loading,
|
|
932
|
+
na,
|
|
933
|
+
next,
|
|
934
|
+
noData,
|
|
935
|
+
of,
|
|
936
|
+
page,
|
|
937
|
+
participants,
|
|
938
|
+
previous,
|
|
939
|
+
prizePool,
|
|
940
|
+
randomlySplit,
|
|
941
|
+
retry,
|
|
942
|
+
rewardDetails,
|
|
943
|
+
rewardRules,
|
|
944
|
+
rewardRulesDesc,
|
|
945
|
+
rewardRulesTitle,
|
|
946
|
+
statusEnded,
|
|
947
|
+
statusOngoing,
|
|
948
|
+
statusUpcoming,
|
|
949
|
+
tabAllTime,
|
|
950
|
+
tabEvent,
|
|
951
|
+
tabQuest,
|
|
952
|
+
tabSprint,
|
|
953
|
+
totalWinners,
|
|
954
|
+
unranked,
|
|
955
|
+
whitelist,
|
|
956
|
+
you
|
|
957
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
958
|
+
function LeaderboardWidget(props) {
|
|
959
|
+
const { widgetId } = props;
|
|
960
|
+
const { functionConfig, cloudTheme, isConfigLoading, configError } = useResolvedWidgetConfig(widgetId);
|
|
961
|
+
const mergedConfig = useMemo(() => {
|
|
962
|
+
return mergeLeaderboardConfig(
|
|
963
|
+
props,
|
|
964
|
+
functionConfig ?? null
|
|
965
|
+
);
|
|
966
|
+
}, [props.config, functionConfig]);
|
|
967
|
+
return /* @__PURE__ */ jsx(
|
|
968
|
+
WidgetShell,
|
|
969
|
+
{
|
|
970
|
+
widgetId,
|
|
971
|
+
isConfigLoading,
|
|
972
|
+
cloudTheme,
|
|
973
|
+
className: "taskon-leaderboard",
|
|
974
|
+
errorMessage: configError ?? (!mergedConfig.config ? "Leaderboard config is required. Please provide config via props or widgetId." : void 0),
|
|
975
|
+
children: /* @__PURE__ */ jsx(
|
|
976
|
+
LeaderboardWidgetInner,
|
|
977
|
+
{
|
|
978
|
+
...props,
|
|
979
|
+
config: mergedConfig.config
|
|
980
|
+
}
|
|
981
|
+
)
|
|
982
|
+
}
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
function LeaderboardWidgetInner({
|
|
986
|
+
config,
|
|
987
|
+
defaultTabId,
|
|
988
|
+
onTabChange,
|
|
989
|
+
onUserClick,
|
|
990
|
+
className = "",
|
|
991
|
+
style
|
|
992
|
+
}) {
|
|
993
|
+
var _a;
|
|
994
|
+
const { userId } = useTaskOnContext();
|
|
995
|
+
const { messages } = useWidgetLocale({
|
|
996
|
+
widgetId: "LeaderboardWidget",
|
|
997
|
+
defaultMessages: enMessages,
|
|
998
|
+
loadMessages: (locale) => __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./locales/en.json": () => Promise.resolve().then(() => en), "./locales/ja.json": () => import("./leaderboardwidget-ja-Bj6gz6y1.js"), "./locales/ko.json": () => import("./leaderboardwidget-ko-f1cLO9ic.js") }), `./locales/${locale}.json`, 3)
|
|
999
|
+
});
|
|
1000
|
+
const [activeTabId, setActiveTabId] = useState(
|
|
1001
|
+
defaultTabId || ((_a = config.leaderboards[0]) == null ? void 0 : _a.id) || ""
|
|
1002
|
+
);
|
|
1003
|
+
const [isRewardModalOpen, setIsRewardModalOpen] = useState(false);
|
|
1004
|
+
const currentTab = useMemo(() => {
|
|
1005
|
+
return config.leaderboards.find((tab) => tab.id === activeTabId);
|
|
1006
|
+
}, [config.leaderboards, activeTabId]);
|
|
1007
|
+
const {
|
|
1008
|
+
page: page2,
|
|
1009
|
+
goToPrevious,
|
|
1010
|
+
goToNext,
|
|
1011
|
+
reset: resetPagination
|
|
1012
|
+
} = usePagination({
|
|
1013
|
+
total: 0,
|
|
1014
|
+
// 初始值,hasPrevious/hasNext 将根据实际 total 重新计算
|
|
1015
|
+
pageSize: config.displayOptions.rows
|
|
1016
|
+
});
|
|
1017
|
+
const {
|
|
1018
|
+
data: leaderboardData,
|
|
1019
|
+
loading: leaderboardLoading,
|
|
1020
|
+
error: leaderboardError,
|
|
1021
|
+
total,
|
|
1022
|
+
refresh: refreshLeaderboard
|
|
1023
|
+
} = useLeaderboard({
|
|
1024
|
+
tabConfig: currentTab,
|
|
1025
|
+
page: page2,
|
|
1026
|
+
pageSize: config.displayOptions.rows
|
|
1027
|
+
});
|
|
1028
|
+
const actualTotalPages = Math.max(1, Math.ceil(total / config.displayOptions.rows));
|
|
1029
|
+
const hasPrevious = page2 > 0;
|
|
1030
|
+
const hasNext = page2 < actualTotalPages - 1;
|
|
1031
|
+
const isSprint = (currentTab == null ? void 0 : currentTab.type) === LeaderboardContentType.LeaderboardSprint;
|
|
1032
|
+
const { rewardSummary, loading: rewardLoading } = useCampaignReward(
|
|
1033
|
+
isSprint ? currentTab == null ? void 0 : currentTab.relatedId : void 0
|
|
1034
|
+
);
|
|
1035
|
+
const handleTabChange = useCallback(
|
|
1036
|
+
(tabId) => {
|
|
1037
|
+
setActiveTabId(tabId);
|
|
1038
|
+
resetPagination();
|
|
1039
|
+
onTabChange == null ? void 0 : onTabChange(tabId);
|
|
1040
|
+
},
|
|
1041
|
+
[onTabChange, resetPagination]
|
|
1042
|
+
);
|
|
1043
|
+
const handleOpenRewardRules = useCallback(() => {
|
|
1044
|
+
setIsRewardModalOpen(true);
|
|
1045
|
+
}, []);
|
|
1046
|
+
const handleCloseRewardRules = useCallback(() => {
|
|
1047
|
+
setIsRewardModalOpen(false);
|
|
1048
|
+
}, []);
|
|
1049
|
+
const allTimeData = useMemo(() => {
|
|
1050
|
+
if (leaderboardData && isAllTimeResponse(leaderboardData)) {
|
|
1051
|
+
return leaderboardData.list;
|
|
1052
|
+
}
|
|
1053
|
+
return void 0;
|
|
1054
|
+
}, [leaderboardData]);
|
|
1055
|
+
const campaignData = useMemo(() => {
|
|
1056
|
+
if (leaderboardData && isCampaignResponse(leaderboardData)) {
|
|
1057
|
+
return leaderboardData.data;
|
|
1058
|
+
}
|
|
1059
|
+
return void 0;
|
|
1060
|
+
}, [leaderboardData]);
|
|
1061
|
+
const participantsCount = useMemo(() => {
|
|
1062
|
+
if (leaderboardData && isCampaignResponse(leaderboardData)) {
|
|
1063
|
+
return leaderboardData.total_user;
|
|
1064
|
+
}
|
|
1065
|
+
return void 0;
|
|
1066
|
+
}, [leaderboardData]);
|
|
1067
|
+
if (leaderboardError) {
|
|
1068
|
+
return /* @__PURE__ */ jsx("div", { className: `taskon-leaderboard taskon-leaderboard--error ${className}`, style, children: /* @__PURE__ */ jsxs("div", { className: "taskon-leaderboard__error", children: [
|
|
1069
|
+
/* @__PURE__ */ jsx("p", { className: "taskon-leaderboard__error-text", children: messages.error }),
|
|
1070
|
+
/* @__PURE__ */ jsx(
|
|
1071
|
+
Button,
|
|
1072
|
+
{
|
|
1073
|
+
variant: "primary",
|
|
1074
|
+
size: "medium",
|
|
1075
|
+
onClick: refreshLeaderboard,
|
|
1076
|
+
className: "taskon-leaderboard__retry-btn",
|
|
1077
|
+
children: messages.retry
|
|
1078
|
+
}
|
|
1079
|
+
)
|
|
1080
|
+
] }) });
|
|
1081
|
+
}
|
|
1082
|
+
if (!currentTab) {
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
1085
|
+
return /* @__PURE__ */ jsxs("div", { className: `taskon-leaderboard ${className}`, style, children: [
|
|
1086
|
+
/* @__PURE__ */ jsx(
|
|
1087
|
+
LeaderboardTabs,
|
|
1088
|
+
{
|
|
1089
|
+
tabs: config.leaderboards,
|
|
1090
|
+
activeTabId,
|
|
1091
|
+
onTabChange: handleTabChange
|
|
1092
|
+
}
|
|
1093
|
+
),
|
|
1094
|
+
/* @__PURE__ */ jsx(
|
|
1095
|
+
LeaderboardHeader,
|
|
1096
|
+
{
|
|
1097
|
+
tabConfig: currentTab,
|
|
1098
|
+
showTitle: config.displayOptions.showTitle,
|
|
1099
|
+
rewardSummary,
|
|
1100
|
+
participantsCount,
|
|
1101
|
+
messages,
|
|
1102
|
+
onOpenRewardRules: isSprint ? handleOpenRewardRules : void 0
|
|
1103
|
+
}
|
|
1104
|
+
),
|
|
1105
|
+
/* @__PURE__ */ jsx(
|
|
1106
|
+
LeaderboardTable,
|
|
1107
|
+
{
|
|
1108
|
+
tabConfig: currentTab,
|
|
1109
|
+
allTimeData,
|
|
1110
|
+
campaignData,
|
|
1111
|
+
currentUserId: userId,
|
|
1112
|
+
messages,
|
|
1113
|
+
onUserClick,
|
|
1114
|
+
rewardTiers: rewardSummary == null ? void 0 : rewardSummary.tiers,
|
|
1115
|
+
loading: leaderboardLoading || rewardLoading,
|
|
1116
|
+
className: "taskon-leaderboard-table"
|
|
1117
|
+
}
|
|
1118
|
+
),
|
|
1119
|
+
/* @__PURE__ */ jsx(
|
|
1120
|
+
LeaderboardPagination,
|
|
1121
|
+
{
|
|
1122
|
+
page: page2,
|
|
1123
|
+
totalPages: actualTotalPages,
|
|
1124
|
+
total,
|
|
1125
|
+
hasPrevious,
|
|
1126
|
+
hasNext,
|
|
1127
|
+
onPrevious: goToPrevious,
|
|
1128
|
+
onNext: goToNext,
|
|
1129
|
+
messages
|
|
1130
|
+
}
|
|
1131
|
+
),
|
|
1132
|
+
isSprint && /* @__PURE__ */ jsx(
|
|
1133
|
+
RewardRulesModal,
|
|
1134
|
+
{
|
|
1135
|
+
isOpen: isRewardModalOpen,
|
|
1136
|
+
onClose: handleCloseRewardRules,
|
|
1137
|
+
rewardSummary,
|
|
1138
|
+
messages
|
|
1139
|
+
}
|
|
1140
|
+
)
|
|
1141
|
+
] });
|
|
1142
|
+
}
|
|
1143
|
+
export {
|
|
1144
|
+
LeaderboardWidget as L,
|
|
1145
|
+
RankBadge as R,
|
|
1146
|
+
UserCell as U,
|
|
1147
|
+
__variableDynamicImportRuntimeHelper as _,
|
|
1148
|
+
useCampaignReward as a,
|
|
1149
|
+
usePagination as b,
|
|
1150
|
+
LeaderboardTabs as c,
|
|
1151
|
+
LeaderboardHeader as d,
|
|
1152
|
+
LeaderboardTable as e,
|
|
1153
|
+
LeaderboardPagination as f,
|
|
1154
|
+
RewardRulesModal as g,
|
|
1155
|
+
useLeaderboard as u
|
|
1156
|
+
};
|