sa2kit 1.6.90 → 1.6.92
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/dist/client-BlkUL2To.d.ts +26 -0
- package/dist/client-DpMIhrlS.d.mts +26 -0
- package/dist/festivalCard/index.js +802 -325
- package/dist/festivalCard/index.js.map +1 -1
- package/dist/festivalCard/index.mjs +783 -306
- package/dist/festivalCard/index.mjs.map +1 -1
- package/dist/festivalCard/miniapp/index.js +162 -21
- package/dist/festivalCard/miniapp/index.js.map +1 -1
- package/dist/festivalCard/miniapp/index.mjs +153 -12
- package/dist/festivalCard/miniapp/index.mjs.map +1 -1
- package/dist/festivalCard/web/index.d.mts +17 -3
- package/dist/festivalCard/web/index.d.ts +17 -3
- package/dist/festivalCard/web/index.js +802 -325
- package/dist/festivalCard/web/index.js.map +1 -1
- package/dist/festivalCard/web/index.mjs +783 -306
- package/dist/festivalCard/web/index.mjs.map +1 -1
- package/dist/index-C7yh6b5Q.d.mts +17 -0
- package/dist/index-CDapUIT5.d.mts +51 -0
- package/dist/index-Cv9jlnNz.d.ts +17 -0
- package/dist/index-D3UbkUai.d.ts +51 -0
- package/dist/index.d.mts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +1925 -355
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1894 -358
- package/dist/index.mjs.map +1 -1
- package/dist/mikuContest/index.d.mts +13 -0
- package/dist/mikuContest/index.d.ts +13 -0
- package/dist/mikuContest/index.js +1310 -0
- package/dist/mikuContest/index.js.map +1 -0
- package/dist/mikuContest/index.mjs +1253 -0
- package/dist/mikuContest/index.mjs.map +1 -0
- package/dist/mikuContest/logic/index.d.mts +32 -0
- package/dist/mikuContest/logic/index.d.ts +32 -0
- package/dist/mikuContest/logic/index.js +511 -0
- package/dist/mikuContest/logic/index.js.map +1 -0
- package/dist/mikuContest/logic/index.mjs +483 -0
- package/dist/mikuContest/logic/index.mjs.map +1 -0
- package/dist/mikuContest/routes/index.d.mts +80 -0
- package/dist/mikuContest/routes/index.d.ts +80 -0
- package/dist/mikuContest/routes/index.js +821 -0
- package/dist/mikuContest/routes/index.js.map +1 -0
- package/dist/mikuContest/routes/index.mjs +791 -0
- package/dist/mikuContest/routes/index.mjs.map +1 -0
- package/dist/mikuContest/server/index.d.mts +766 -0
- package/dist/mikuContest/server/index.d.ts +766 -0
- package/dist/mikuContest/server/index.js +705 -0
- package/dist/mikuContest/server/index.js.map +1 -0
- package/dist/mikuContest/server/index.mjs +672 -0
- package/dist/mikuContest/server/index.mjs.map +1 -0
- package/dist/mikuContest/service/index.d.mts +30 -0
- package/dist/mikuContest/service/index.d.ts +30 -0
- package/dist/mikuContest/service/index.js +139 -0
- package/dist/mikuContest/service/index.js.map +1 -0
- package/dist/mikuContest/service/index.mjs +135 -0
- package/dist/mikuContest/service/index.mjs.map +1 -0
- package/dist/mikuContest/types/index.d.mts +179 -0
- package/dist/mikuContest/types/index.d.ts +179 -0
- package/dist/mikuContest/types/index.js +4 -0
- package/dist/mikuContest/types/index.js.map +1 -0
- package/dist/mikuContest/types/index.mjs +3 -0
- package/dist/mikuContest/types/index.mjs.map +1 -0
- package/dist/mikuContest/ui/miniapp/index.d.mts +3 -0
- package/dist/mikuContest/ui/miniapp/index.d.ts +3 -0
- package/dist/mikuContest/ui/miniapp/index.js +566 -0
- package/dist/mikuContest/ui/miniapp/index.js.map +1 -0
- package/dist/mikuContest/ui/miniapp/index.mjs +540 -0
- package/dist/mikuContest/ui/miniapp/index.mjs.map +1 -0
- package/dist/mikuContest/ui/web/index.d.mts +4 -0
- package/dist/mikuContest/ui/web/index.d.ts +4 -0
- package/dist/mikuContest/ui/web/index.js +353 -0
- package/dist/mikuContest/ui/web/index.js.map +1 -0
- package/dist/mikuContest/ui/web/index.mjs +343 -0
- package/dist/mikuContest/ui/web/index.mjs.map +1 -0
- package/dist/service-D7DM1wW-.d.ts +38 -0
- package/dist/service-DPr2rlvH.d.mts +38 -0
- package/dist/types-BS7Xz09b.d.mts +14 -0
- package/dist/types-k4koMp4m.d.ts +14 -0
- package/package.json +41 -1
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import React4, { useMemo, useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
// src/mikuContest/ui/web/components/MikuContestDashboard.tsx
|
|
4
|
+
var MikuContestDashboard = ({ snapshot }) => {
|
|
5
|
+
return /* @__PURE__ */ React4.createElement("div", null, /* @__PURE__ */ React4.createElement("h2", null, snapshot.contest.name), /* @__PURE__ */ React4.createElement("p", null, snapshot.contest.theme), /* @__PURE__ */ React4.createElement("p", null, "\u6295\u7A3F\u6570\uFF1A", snapshot.submissions.length), /* @__PURE__ */ React4.createElement("p", null, "\u516C\u544A\u6570\uFF1A", snapshot.announcements.length), /* @__PURE__ */ React4.createElement("ul", null, snapshot.leaderboard.map((item) => /* @__PURE__ */ React4.createElement("li", { key: item.submissionId }, "#", item.rank, " ", item.title, " - ", item.voteCount, "\u7968"))));
|
|
6
|
+
};
|
|
7
|
+
var MikuContestDashboard_default = MikuContestDashboard;
|
|
8
|
+
|
|
9
|
+
// src/mikuContest/service/api/client.ts
|
|
10
|
+
var toQueryString = (filter) => {
|
|
11
|
+
if (!filter) return "";
|
|
12
|
+
const params = new URLSearchParams();
|
|
13
|
+
if (filter.status) params.set("status", filter.status);
|
|
14
|
+
if (filter.type) params.set("type", filter.type);
|
|
15
|
+
if (filter.authorId) params.set("authorId", filter.authorId);
|
|
16
|
+
if (filter.authorKeyword) params.set("authorKeyword", filter.authorKeyword);
|
|
17
|
+
if (filter.titleKeyword) params.set("titleKeyword", filter.titleKeyword);
|
|
18
|
+
const query = params.toString();
|
|
19
|
+
return query ? `?${query}` : "";
|
|
20
|
+
};
|
|
21
|
+
var unwrap = (result) => {
|
|
22
|
+
if (!result.success || result.data === void 0) {
|
|
23
|
+
throw new Error(result.error || "\u8BF7\u6C42\u5931\u8D25");
|
|
24
|
+
}
|
|
25
|
+
return result.data;
|
|
26
|
+
};
|
|
27
|
+
var createMikuContestApiClient = (basePath, requester) => {
|
|
28
|
+
return {
|
|
29
|
+
async getSnapshot() {
|
|
30
|
+
const result = await requester(`${basePath}/contest`, { method: "GET" });
|
|
31
|
+
return unwrap(result);
|
|
32
|
+
},
|
|
33
|
+
async updateContestConfig(patch) {
|
|
34
|
+
const result = await requester(`${basePath}/contest`, {
|
|
35
|
+
method: "PATCH",
|
|
36
|
+
body: patch
|
|
37
|
+
});
|
|
38
|
+
return unwrap(result);
|
|
39
|
+
},
|
|
40
|
+
async createSubmission(input, mode = "web") {
|
|
41
|
+
const result = await requester(`${basePath}/submissions`, {
|
|
42
|
+
method: "POST",
|
|
43
|
+
body: { payload: input, mode }
|
|
44
|
+
});
|
|
45
|
+
return unwrap(result);
|
|
46
|
+
},
|
|
47
|
+
async listSubmissions(filter) {
|
|
48
|
+
const result = await requester(
|
|
49
|
+
`${basePath}/submissions${toQueryString(filter)}`,
|
|
50
|
+
{ method: "GET" }
|
|
51
|
+
);
|
|
52
|
+
return unwrap(result);
|
|
53
|
+
},
|
|
54
|
+
async reviewSubmission(input) {
|
|
55
|
+
const result = await requester(`${basePath}/submissions/review`, {
|
|
56
|
+
method: "POST",
|
|
57
|
+
body: input
|
|
58
|
+
});
|
|
59
|
+
return unwrap(result);
|
|
60
|
+
},
|
|
61
|
+
async vote(input) {
|
|
62
|
+
const result = await requester(`${basePath}/votes`, {
|
|
63
|
+
method: "POST",
|
|
64
|
+
body: input
|
|
65
|
+
});
|
|
66
|
+
return unwrap(result);
|
|
67
|
+
},
|
|
68
|
+
async setVoterRestriction(input) {
|
|
69
|
+
const result = await requester(`${basePath}/admin/voter-restrictions`, {
|
|
70
|
+
method: "POST",
|
|
71
|
+
body: input
|
|
72
|
+
});
|
|
73
|
+
return unwrap(result);
|
|
74
|
+
},
|
|
75
|
+
async resetVotes(input) {
|
|
76
|
+
const result = await requester(
|
|
77
|
+
`${basePath}/admin/votes/reset`,
|
|
78
|
+
{
|
|
79
|
+
method: "POST",
|
|
80
|
+
body: input
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
return unwrap(result);
|
|
84
|
+
},
|
|
85
|
+
async exportSubmissions(filter) {
|
|
86
|
+
const response = await fetch(`${basePath}/admin/submissions/export${toQueryString(filter)}`);
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
throw new Error(`\u5BFC\u51FA\u5931\u8D25: ${response.status}`);
|
|
89
|
+
}
|
|
90
|
+
return response.arrayBuffer();
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/mikuContest/service/web/client.ts
|
|
96
|
+
var defaultRequester = (options) => {
|
|
97
|
+
const baseUrl = options.baseUrl || "";
|
|
98
|
+
const commonHeaders = options.headers || {};
|
|
99
|
+
return async (url, requestOptions) => {
|
|
100
|
+
const response = await fetch(`${baseUrl}${url}`, {
|
|
101
|
+
method: requestOptions?.method || "GET",
|
|
102
|
+
headers: {
|
|
103
|
+
"Content-Type": "application/json",
|
|
104
|
+
...commonHeaders
|
|
105
|
+
},
|
|
106
|
+
body: requestOptions?.body ? JSON.stringify(requestOptions.body) : void 0
|
|
107
|
+
});
|
|
108
|
+
const json = await response.json();
|
|
109
|
+
return json;
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
var createMikuContestWebClient = (options = {}) => {
|
|
113
|
+
const basePath = options.basePath || "/api/miku-contest";
|
|
114
|
+
return createMikuContestApiClient(basePath, defaultRequester(options));
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// src/mikuContest/ui/web/pages/MikuContestAudiencePage.tsx
|
|
118
|
+
var MikuContestAudiencePage = ({
|
|
119
|
+
client,
|
|
120
|
+
voterId,
|
|
121
|
+
title = "\u89C2\u4F17\u6295\u7968\u533A"
|
|
122
|
+
}) => {
|
|
123
|
+
const api = useMemo(() => client || createMikuContestWebClient(), [client]);
|
|
124
|
+
const [snapshot, setSnapshot] = useState(null);
|
|
125
|
+
const [loading, setLoading] = useState(false);
|
|
126
|
+
const [error, setError] = useState(null);
|
|
127
|
+
const approvedWorks = useMemo(() => {
|
|
128
|
+
if (!snapshot) return [];
|
|
129
|
+
return snapshot.submissions.filter((item) => item.status === "approved");
|
|
130
|
+
}, [snapshot]);
|
|
131
|
+
const loadSnapshot = async () => {
|
|
132
|
+
setLoading(true);
|
|
133
|
+
setError(null);
|
|
134
|
+
try {
|
|
135
|
+
const data = await api.getSnapshot();
|
|
136
|
+
setSnapshot(data);
|
|
137
|
+
} catch (e) {
|
|
138
|
+
setError(e.message);
|
|
139
|
+
} finally {
|
|
140
|
+
setLoading(false);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
void loadSnapshot();
|
|
145
|
+
}, []);
|
|
146
|
+
const handleVote = async (submission) => {
|
|
147
|
+
if (!snapshot) return;
|
|
148
|
+
try {
|
|
149
|
+
await api.vote({
|
|
150
|
+
contestId: snapshot.contest.id,
|
|
151
|
+
submissionId: submission.id,
|
|
152
|
+
voterId
|
|
153
|
+
});
|
|
154
|
+
await loadSnapshot();
|
|
155
|
+
} catch (e) {
|
|
156
|
+
setError(e.message);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
return /* @__PURE__ */ React4.createElement("section", null, /* @__PURE__ */ React4.createElement("h2", null, title), /* @__PURE__ */ React4.createElement("button", { onClick: () => void loadSnapshot(), disabled: loading }, loading ? "\u5237\u65B0\u4E2D..." : "\u5237\u65B0\u6570\u636E"), error ? /* @__PURE__ */ React4.createElement("p", { style: { color: "crimson" } }, "\u9519\u8BEF\uFF1A", error) : null, !snapshot ? null : /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement("p", null, "\u8D5B\u4E8B\uFF1A", snapshot.contest.name, "\uFF5C\u4E3B\u9898\uFF1A", snapshot.contest.theme), /* @__PURE__ */ React4.createElement("p", null, "\u5DF2\u8FC7\u5BA1\u4F5C\u54C1\uFF1A", approvedWorks.length, "\uFF5C\u6BCF\u65E5\u4E0A\u9650\uFF1A", snapshot.contest.votingRules.maxVotesPerDay), /* @__PURE__ */ React4.createElement("ul", null, approvedWorks.map((work) => /* @__PURE__ */ React4.createElement("li", { key: work.id }, /* @__PURE__ */ React4.createElement("strong", null, work.title), "\uFF08", work.authorNickname, "\uFF09- \u5F53\u524D ", work.voteCount, " \u7968", " ", /* @__PURE__ */ React4.createElement("button", { onClick: () => void handleVote(work) }, "\u6295\u7968"))))));
|
|
160
|
+
};
|
|
161
|
+
var MikuContestAudiencePage_default = MikuContestAudiencePage;
|
|
162
|
+
var workTypes = ["visual", "video", "text", "audio"];
|
|
163
|
+
var MikuContestArtistPage = ({
|
|
164
|
+
client,
|
|
165
|
+
authorId,
|
|
166
|
+
authorNickname,
|
|
167
|
+
title = "\u753B\u5E08\u6295\u7A3F\u533A"
|
|
168
|
+
}) => {
|
|
169
|
+
const api = useMemo(() => client || createMikuContestWebClient(), [client]);
|
|
170
|
+
const [snapshot, setSnapshot] = useState(null);
|
|
171
|
+
const [mySubmissions, setMySubmissions] = useState([]);
|
|
172
|
+
const [submitting, setSubmitting] = useState(false);
|
|
173
|
+
const [loading, setLoading] = useState(false);
|
|
174
|
+
const [error, setError] = useState(null);
|
|
175
|
+
const [titleInput, setTitleInput] = useState("");
|
|
176
|
+
const [descInput, setDescInput] = useState("");
|
|
177
|
+
const [coverImage, setCoverImage] = useState("");
|
|
178
|
+
const [workType, setWorkType] = useState("visual");
|
|
179
|
+
const loadData = async () => {
|
|
180
|
+
setLoading(true);
|
|
181
|
+
setError(null);
|
|
182
|
+
try {
|
|
183
|
+
const [contest, mine] = await Promise.all([
|
|
184
|
+
api.getSnapshot(),
|
|
185
|
+
api.listSubmissions({ authorId })
|
|
186
|
+
]);
|
|
187
|
+
setSnapshot(contest);
|
|
188
|
+
setMySubmissions(mine);
|
|
189
|
+
} catch (e) {
|
|
190
|
+
setError(e.message);
|
|
191
|
+
} finally {
|
|
192
|
+
setLoading(false);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
void loadData();
|
|
197
|
+
}, []);
|
|
198
|
+
const submitWork = async () => {
|
|
199
|
+
if (!snapshot) return;
|
|
200
|
+
const payload = {
|
|
201
|
+
contestId: snapshot.contest.id,
|
|
202
|
+
authorId,
|
|
203
|
+
authorNickname,
|
|
204
|
+
title: titleInput,
|
|
205
|
+
description: descInput,
|
|
206
|
+
type: workType,
|
|
207
|
+
tags: ["web"],
|
|
208
|
+
content: {
|
|
209
|
+
coverImage,
|
|
210
|
+
images: coverImage ? [coverImage] : void 0
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
setSubmitting(true);
|
|
214
|
+
setError(null);
|
|
215
|
+
try {
|
|
216
|
+
await api.createSubmission(payload, "web");
|
|
217
|
+
setTitleInput("");
|
|
218
|
+
setDescInput("");
|
|
219
|
+
setCoverImage("");
|
|
220
|
+
await loadData();
|
|
221
|
+
} catch (e) {
|
|
222
|
+
setError(e.message);
|
|
223
|
+
} finally {
|
|
224
|
+
setSubmitting(false);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
return /* @__PURE__ */ React4.createElement("section", null, /* @__PURE__ */ React4.createElement("h2", null, title), /* @__PURE__ */ React4.createElement("button", { onClick: () => void loadData(), disabled: loading }, loading ? "\u5237\u65B0\u4E2D..." : "\u5237\u65B0\u6570\u636E"), error ? /* @__PURE__ */ React4.createElement("p", { style: { color: "crimson" } }, "\u9519\u8BEF\uFF1A", error) : null, /* @__PURE__ */ React4.createElement("div", null, /* @__PURE__ */ React4.createElement("h3", null, "\u65B0\u5EFA\u6295\u7A3F"), /* @__PURE__ */ React4.createElement("input", { value: titleInput, onChange: (e) => setTitleInput(e.target.value), placeholder: "\u4F5C\u54C1\u6807\u9898" }), /* @__PURE__ */ React4.createElement("br", null), /* @__PURE__ */ React4.createElement("textarea", { value: descInput, onChange: (e) => setDescInput(e.target.value), placeholder: "\u4F5C\u54C1\u7B80\u4ECB" }), /* @__PURE__ */ React4.createElement("br", null), /* @__PURE__ */ React4.createElement("input", { value: coverImage, onChange: (e) => setCoverImage(e.target.value), placeholder: "\u5C01\u9762 URL" }), /* @__PURE__ */ React4.createElement("br", null), /* @__PURE__ */ React4.createElement("select", { value: workType, onChange: (e) => setWorkType(e.target.value) }, workTypes.map((item) => /* @__PURE__ */ React4.createElement("option", { value: item, key: item }, item))), /* @__PURE__ */ React4.createElement("button", { onClick: () => void submitWork(), disabled: submitting || !snapshot }, submitting ? "\u63D0\u4EA4\u4E2D..." : "\u63D0\u4EA4\u7A3F\u4EF6")), /* @__PURE__ */ React4.createElement("div", null, /* @__PURE__ */ React4.createElement("h3", null, "\u6211\u7684\u6295\u7A3F\uFF08", mySubmissions.length, "\uFF09"), /* @__PURE__ */ React4.createElement("ul", null, mySubmissions.map((item) => /* @__PURE__ */ React4.createElement("li", { key: item.id }, item.title, "\uFF5C\u72B6\u6001\uFF1A", item.status, "\uFF5C\u7968\u6570\uFF1A", item.voteCount, item.rejectReason ? `\uFF5C\u9A73\u56DE\uFF1A${item.rejectReason}` : "")))));
|
|
228
|
+
};
|
|
229
|
+
var MikuContestArtistPage_default = MikuContestArtistPage;
|
|
230
|
+
var MikuContestAdminPage = ({
|
|
231
|
+
client,
|
|
232
|
+
adminId,
|
|
233
|
+
title = "\u7BA1\u7406\u5458\u9762\u677F"
|
|
234
|
+
}) => {
|
|
235
|
+
const api = useMemo(() => client || createMikuContestWebClient(), [client]);
|
|
236
|
+
const [snapshot, setSnapshot] = useState(null);
|
|
237
|
+
const [submissions, setSubmissions] = useState([]);
|
|
238
|
+
const [loading, setLoading] = useState(false);
|
|
239
|
+
const [error, setError] = useState(null);
|
|
240
|
+
const [voterId, setVoterId] = useState("");
|
|
241
|
+
const loadData = async () => {
|
|
242
|
+
setLoading(true);
|
|
243
|
+
setError(null);
|
|
244
|
+
try {
|
|
245
|
+
const [contest, list] = await Promise.all([api.getSnapshot(), api.listSubmissions()]);
|
|
246
|
+
setSnapshot(contest);
|
|
247
|
+
setSubmissions(list);
|
|
248
|
+
} catch (e) {
|
|
249
|
+
setError(e.message);
|
|
250
|
+
} finally {
|
|
251
|
+
setLoading(false);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
useEffect(() => {
|
|
255
|
+
void loadData();
|
|
256
|
+
}, []);
|
|
257
|
+
const review = async (item, action) => {
|
|
258
|
+
try {
|
|
259
|
+
await api.reviewSubmission({
|
|
260
|
+
submissionId: item.id,
|
|
261
|
+
reviewerId: adminId,
|
|
262
|
+
action,
|
|
263
|
+
rejectReason: action === "reject" ? "\u7BA1\u7406\u5458\u9A73\u56DE" : void 0
|
|
264
|
+
});
|
|
265
|
+
await loadData();
|
|
266
|
+
} catch (e) {
|
|
267
|
+
setError(e.message);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
const toggleVoting = async (enabled) => {
|
|
271
|
+
if (!snapshot) return;
|
|
272
|
+
try {
|
|
273
|
+
await api.updateContestConfig({
|
|
274
|
+
toggles: {
|
|
275
|
+
...snapshot.contest.toggles,
|
|
276
|
+
votingEnabled: enabled
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
await loadData();
|
|
280
|
+
} catch (e) {
|
|
281
|
+
setError(e.message);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
const setRestriction = async (banned) => {
|
|
285
|
+
if (!snapshot || !voterId.trim()) return;
|
|
286
|
+
try {
|
|
287
|
+
await api.setVoterRestriction({
|
|
288
|
+
voterId: voterId.trim(),
|
|
289
|
+
banned,
|
|
290
|
+
reason: banned ? "\u7BA1\u7406\u5458\u624B\u52A8\u5C01\u7981" : "\u7BA1\u7406\u5458\u89E3\u9664\u5C01\u7981",
|
|
291
|
+
operatorId: adminId
|
|
292
|
+
});
|
|
293
|
+
setVoterId("");
|
|
294
|
+
} catch (e) {
|
|
295
|
+
setError(e.message);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
const resetVotesByVoter = async () => {
|
|
299
|
+
if (!voterId.trim()) return;
|
|
300
|
+
try {
|
|
301
|
+
await api.resetVotes({ voterId: voterId.trim() });
|
|
302
|
+
setVoterId("");
|
|
303
|
+
await loadData();
|
|
304
|
+
} catch (e) {
|
|
305
|
+
setError(e.message);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
const exportExcel = async () => {
|
|
309
|
+
try {
|
|
310
|
+
const data = await api.exportSubmissions();
|
|
311
|
+
const blob = new Blob([data], {
|
|
312
|
+
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
313
|
+
});
|
|
314
|
+
const url = URL.createObjectURL(blob);
|
|
315
|
+
const a = document.createElement("a");
|
|
316
|
+
a.href = url;
|
|
317
|
+
a.download = "miku-submissions.xlsx";
|
|
318
|
+
a.click();
|
|
319
|
+
URL.revokeObjectURL(url);
|
|
320
|
+
} catch (e) {
|
|
321
|
+
setError(e.message);
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
return /* @__PURE__ */ React4.createElement("section", null, /* @__PURE__ */ React4.createElement("h2", null, title), /* @__PURE__ */ React4.createElement("button", { onClick: () => void loadData(), disabled: loading }, loading ? "\u5237\u65B0\u4E2D..." : "\u5237\u65B0\u6570\u636E"), error ? /* @__PURE__ */ React4.createElement("p", { style: { color: "crimson" } }, "\u9519\u8BEF\uFF1A", error) : null, /* @__PURE__ */ React4.createElement("div", null, /* @__PURE__ */ React4.createElement("h3", null, "\u8D5B\u4E8B\u5F00\u5173"), /* @__PURE__ */ React4.createElement("button", { onClick: () => void toggleVoting(true), disabled: !snapshot }, "\u5F00\u542F\u6295\u7968"), /* @__PURE__ */ React4.createElement("button", { onClick: () => void toggleVoting(false), disabled: !snapshot }, "\u5173\u95ED\u6295\u7968")), /* @__PURE__ */ React4.createElement("div", null, /* @__PURE__ */ React4.createElement("h3", null, "\u6295\u7A3F\u5BA1\u6838\uFF08", submissions.length, "\uFF09"), /* @__PURE__ */ React4.createElement("ul", null, submissions.map((item) => /* @__PURE__ */ React4.createElement("li", { key: item.id }, /* @__PURE__ */ React4.createElement("strong", null, item.title), "\uFF5C\u4F5C\u8005\uFF1A", item.authorNickname, "\uFF5C\u72B6\u6001\uFF1A", item.status, "\uFF5C\u7968\u6570\uFF1A", item.voteCount, item.status === "pending" ? /* @__PURE__ */ React4.createElement(React4.Fragment, null, " ", /* @__PURE__ */ React4.createElement("button", { onClick: () => void review(item, "approve") }, "\u901A\u8FC7"), /* @__PURE__ */ React4.createElement("button", { onClick: () => void review(item, "reject") }, "\u9A73\u56DE")) : null)))), /* @__PURE__ */ React4.createElement("div", null, /* @__PURE__ */ React4.createElement("h3", null, "\u6295\u7968\u98CE\u63A7"), /* @__PURE__ */ React4.createElement("input", { value: voterId, onChange: (e) => setVoterId(e.target.value), placeholder: "voterId" }), /* @__PURE__ */ React4.createElement("button", { onClick: () => void setRestriction(true), disabled: !snapshot }, "\u5C01\u7981\u6295\u7968"), /* @__PURE__ */ React4.createElement("button", { onClick: () => void setRestriction(false), disabled: !snapshot }, "\u89E3\u9664\u5C01\u7981"), /* @__PURE__ */ React4.createElement("button", { onClick: () => void resetVotesByVoter() }, "\u6E05\u96F6\u8BE5\u7528\u6237\u7968\u6570")), /* @__PURE__ */ React4.createElement("div", null, /* @__PURE__ */ React4.createElement("h3", null, "\u5BFC\u51FA"), /* @__PURE__ */ React4.createElement("button", { onClick: () => void exportExcel() }, "\u5BFC\u51FA\u6295\u7A3F Excel")));
|
|
325
|
+
};
|
|
326
|
+
var MikuContestAdminPage_default = MikuContestAdminPage;
|
|
327
|
+
|
|
328
|
+
// src/mikuContest/ui/web/pages/MikuContestPage.tsx
|
|
329
|
+
var MikuContestPage = ({
|
|
330
|
+
defaultView = "audience",
|
|
331
|
+
viewerVoterId = "viewer-demo",
|
|
332
|
+
artistId = "artist-demo",
|
|
333
|
+
artistNickname = "Demo \u753B\u5E08",
|
|
334
|
+
adminId = "admin-demo"
|
|
335
|
+
}) => {
|
|
336
|
+
const [view, setView] = useState(defaultView);
|
|
337
|
+
return /* @__PURE__ */ React4.createElement("div", null, /* @__PURE__ */ React4.createElement("h1", null, "Miku Contest"), /* @__PURE__ */ React4.createElement("div", null, /* @__PURE__ */ React4.createElement("button", { onClick: () => setView("audience") }, "\u89C2\u4F17\u7AEF"), /* @__PURE__ */ React4.createElement("button", { onClick: () => setView("artist") }, "\u753B\u5E08\u7AEF"), /* @__PURE__ */ React4.createElement("button", { onClick: () => setView("admin") }, "\u7BA1\u7406\u5458\u7AEF")), view === "audience" ? /* @__PURE__ */ React4.createElement(MikuContestAudiencePage_default, { voterId: viewerVoterId }) : null, view === "artist" ? /* @__PURE__ */ React4.createElement(MikuContestArtistPage_default, { authorId: artistId, authorNickname: artistNickname }) : null, view === "admin" ? /* @__PURE__ */ React4.createElement(MikuContestAdminPage_default, { adminId }) : null);
|
|
338
|
+
};
|
|
339
|
+
var MikuContestPage_default = MikuContestPage;
|
|
340
|
+
|
|
341
|
+
export { MikuContestAdminPage_default as MikuContestAdminPage, MikuContestArtistPage_default as MikuContestArtistPage, MikuContestAudiencePage_default as MikuContestAudiencePage, MikuContestDashboard_default as MikuContestDashboard, MikuContestPage_default as MikuContestPage };
|
|
342
|
+
//# sourceMappingURL=index.mjs.map
|
|
343
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/mikuContest/ui/web/components/MikuContestDashboard.tsx","../../../../src/mikuContest/service/api/client.ts","../../../../src/mikuContest/service/web/client.ts","../../../../src/mikuContest/ui/web/pages/MikuContestAudiencePage.tsx","../../../../src/mikuContest/ui/web/pages/MikuContestArtistPage.tsx","../../../../src/mikuContest/ui/web/pages/MikuContestAdminPage.tsx","../../../../src/mikuContest/ui/web/pages/MikuContestPage.tsx"],"names":["React","useMemo","useState","useEffect"],"mappings":";;;AAOA,IAAM,oBAAA,GAA4D,CAAC,EAAE,QAAA,EAAS,KAAM;AAClF,EAAA,uBACEA,MAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACCA,MAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAI,QAAA,CAAS,QAAQ,IAAK,CAAA,kBAC3BA,MAAA,CAAA,aAAA,CAAC,GAAA,EAAA,IAAA,EAAG,QAAA,CAAS,OAAA,CAAQ,KAAM,CAAA,kBAC3BA,MAAA,CAAA,aAAA,CAAC,GAAA,EAAA,IAAA,EAAE,0BAAA,EAAK,QAAA,CAAS,WAAA,CAAY,MAAO,CAAA,kBACpCA,MAAA,CAAA,aAAA,CAAC,GAAA,EAAA,IAAA,EAAE,0BAAA,EAAK,QAAA,CAAS,aAAA,CAAc,MAAO,CAAA,kBACtCA,MAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EACE,QAAA,CAAS,WAAA,CAAY,GAAA,CAAI,CAAC,IAAA,qBACzBA,MAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,GAAA,EAAK,IAAA,CAAK,YAAA,EAAA,EAAc,KACxB,IAAA,CAAK,IAAA,EAAK,GAAA,EAAE,IAAA,CAAK,KAAA,EAAM,KAAA,EAAI,KAAK,SAAA,EAAU,QAC9C,CACD,CACH,CACF,CAAA;AAEJ,CAAA;AAEA,IAAO,4BAAA,GAAQ;;;ACMf,IAAM,aAAA,GAAgB,CAAC,MAAA,KAA0C;AAC/D,EAAA,IAAI,CAAC,QAAQ,OAAO,EAAA;AACpB,EAAA,MAAM,MAAA,GAAS,IAAI,eAAA,EAAgB;AACnC,EAAA,IAAI,OAAO,MAAA,EAAQ,MAAA,CAAO,GAAA,CAAI,QAAA,EAAU,OAAO,MAAM,CAAA;AACrD,EAAA,IAAI,OAAO,IAAA,EAAM,MAAA,CAAO,GAAA,CAAI,MAAA,EAAQ,OAAO,IAAI,CAAA;AAC/C,EAAA,IAAI,OAAO,QAAA,EAAU,MAAA,CAAO,GAAA,CAAI,UAAA,EAAY,OAAO,QAAQ,CAAA;AAC3D,EAAA,IAAI,OAAO,aAAA,EAAe,MAAA,CAAO,GAAA,CAAI,eAAA,EAAiB,OAAO,aAAa,CAAA;AAC1E,EAAA,IAAI,OAAO,YAAA,EAAc,MAAA,CAAO,GAAA,CAAI,cAAA,EAAgB,OAAO,YAAY,CAAA;AACvE,EAAA,MAAM,KAAA,GAAQ,OAAO,QAAA,EAAS;AAC9B,EAAA,OAAO,KAAA,GAAQ,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA,GAAK,EAAA;AAC/B,CAAA;AAQA,IAAM,MAAA,GAAS,CAAI,MAAA,KAA8B;AAC/C,EAAA,IAAI,CAAC,MAAA,CAAO,OAAA,IAAW,MAAA,CAAO,SAAS,MAAA,EAAW;AAChD,IAAA,MAAM,IAAI,KAAA,CAAM,MAAA,CAAO,KAAA,IAAS,0BAAM,CAAA;AAAA,EACxC;AACA,EAAA,OAAO,MAAA,CAAO,IAAA;AAChB,CAAA;AAEO,IAAM,0BAAA,GAA6B,CACxC,QAAA,EACA,SAAA,KACyB;AACzB,EAAA,OAAO;AAAA,IACL,MAAM,WAAA,GAAc;AAClB,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,CAAiD,CAAA,EAAG,QAAQ,CAAA,QAAA,CAAA,EAAY,EAAE,MAAA,EAAQ,KAAA,EAAO,CAAA;AAC9G,MAAA,OAAO,OAAO,MAAM,CAAA;AAAA,IACtB,CAAA;AAAA,IACA,MAAM,oBAAoB,KAAA,EAAO;AAC/B,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,CAA0C,CAAA,EAAG,QAAQ,CAAA,QAAA,CAAA,EAAY;AAAA,QACpF,MAAA,EAAQ,OAAA;AAAA,QACR,IAAA,EAAM;AAAA,OACP,CAAA;AACD,MAAA,OAAO,OAAO,MAAM,CAAA;AAAA,IACtB,CAAA;AAAA,IACA,MAAM,gBAAA,CAAiB,KAAA,EAAO,IAAA,GAAO,KAAA,EAAO;AAC1C,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,CAAuC,CAAA,EAAG,QAAQ,CAAA,YAAA,CAAA,EAAgB;AAAA,QACrF,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM,EAAE,OAAA,EAAS,KAAA,EAAO,IAAA;AAAK,OAC9B,CAAA;AACD,MAAA,OAAO,OAAO,MAAM,CAAA;AAAA,IACtB,CAAA;AAAA,IACA,MAAM,gBAAgB,MAAA,EAAQ;AAC5B,MAAA,MAAM,SAAS,MAAM,SAAA;AAAA,QACnB,CAAA,EAAG,QAAQ,CAAA,YAAA,EAAe,aAAA,CAAc,MAAM,CAAC,CAAA,CAAA;AAAA,QAC/C,EAAE,QAAQ,KAAA;AAAM,OAClB;AACA,MAAA,OAAO,OAAO,MAAM,CAAA;AAAA,IACtB,CAAA;AAAA,IACA,MAAM,iBAAiB,KAAA,EAAO;AAC5B,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,CAAuC,CAAA,EAAG,QAAQ,CAAA,mBAAA,CAAA,EAAuB;AAAA,QAC5F,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM;AAAA,OACP,CAAA;AACD,MAAA,OAAO,OAAO,MAAM,CAAA;AAAA,IACtB,CAAA;AAAA,IACA,MAAM,KAAK,KAAA,EAAO;AAChB,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,CAAuC,CAAA,EAAG,QAAQ,CAAA,MAAA,CAAA,EAAU;AAAA,QAC/E,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM;AAAA,OACP,CAAA;AACD,MAAA,OAAO,OAAO,MAAM,CAAA;AAAA,IACtB,CAAA;AAAA,IACA,MAAM,oBAAoB,KAAA,EAAO;AAC/B,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,CAA6C,CAAA,EAAG,QAAQ,CAAA,yBAAA,CAAA,EAA6B;AAAA,QACxG,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM;AAAA,OACP,CAAA;AACD,MAAA,OAAO,OAAO,MAAM,CAAA;AAAA,IACtB,CAAA;AAAA,IACA,MAAM,WAAW,KAAA,EAAO;AACtB,MAAA,MAAM,SAAS,MAAM,SAAA;AAAA,QACnB,GAAG,QAAQ,CAAA,kBAAA,CAAA;AAAA,QACX;AAAA,UACE,MAAA,EAAQ,MAAA;AAAA,UACR,IAAA,EAAM;AAAA;AACR,OACF;AACA,MAAA,OAAO,OAAO,MAAM,CAAA;AAAA,IACtB,CAAA;AAAA,IACA,MAAM,kBAAkB,MAAA,EAAQ;AAC9B,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,CAAA,EAAG,QAAQ,CAAA,yBAAA,EAA4B,aAAA,CAAc,MAAM,CAAC,CAAA,CAAE,CAAA;AAC3F,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,0BAAA,EAAS,QAAA,CAAS,MAAM,CAAA,CAAE,CAAA;AAAA,MAC5C;AACA,MAAA,OAAO,SAAS,WAAA,EAAY;AAAA,IAC9B;AAAA,GACF;AACF,CAAA;;;ACrHA,IAAM,gBAAA,GAAmB,CAAC,OAAA,KAAoD;AAC5E,EAAA,MAAM,OAAA,GAAU,QAAQ,OAAA,IAAW,EAAA;AACnC,EAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,OAAA,IAAW,EAAC;AAE1C,EAAA,OAAO,OAAU,KAAa,cAAA,KAAuF;AACnH,IAAA,MAAM,WAAW,MAAM,KAAA,CAAM,GAAG,OAAO,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI;AAAA,MAC/C,MAAA,EAAQ,gBAAgB,MAAA,IAAU,KAAA;AAAA,MAClC,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB,kBAAA;AAAA,QAChB,GAAG;AAAA,OACL;AAAA,MACA,MAAM,cAAA,EAAgB,IAAA,GAAO,KAAK,SAAA,CAAU,cAAA,CAAe,IAAI,CAAA,GAAI;AAAA,KACpE,CAAA;AAED,IAAA,MAAM,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAA,EAAK;AAClC,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AACF,CAAA;AAEO,IAAM,0BAAA,GAA6B,CAAC,OAAA,GAAuC,EAAC,KAAM;AACvF,EAAA,MAAM,QAAA,GAAW,QAAQ,QAAA,IAAY,mBAAA;AACrC,EAAA,OAAO,0BAAA,CAA2B,QAAA,EAAU,gBAAA,CAAiB,OAAO,CAAC,CAAA;AACvE,CAAA;;;ACnBA,IAAM,0BAAkE,CAAC;AAAA,EACvE,MAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA,GAAQ;AACV,CAAA,KAAM;AACJ,EAAA,MAAM,GAAA,GAAM,QAAQ,MAAM,MAAA,IAAU,4BAA2B,EAAG,CAAC,MAAM,CAAC,CAAA;AAE1E,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAA0C,IAAI,CAAA;AAC9E,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAwB,IAAI,CAAA;AAEtD,EAAA,MAAM,aAAA,GAAgB,QAAQ,MAAM;AAClC,IAAA,IAAI,CAAC,QAAA,EAAU,OAAO,EAAC;AACvB,IAAA,OAAO,SAAS,WAAA,CAAY,MAAA,CAAO,CAAC,IAAA,KAAS,IAAA,CAAK,WAAW,UAAU,CAAA;AAAA,EACzE,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,MAAM,eAAe,YAAY;AAC/B,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,WAAA,EAAY;AACnC,MAAA,WAAA,CAAY,IAAI,CAAA;AAAA,IAClB,SAAS,CAAA,EAAG;AACV,MAAA,QAAA,CAAU,EAAY,OAAO,CAAA;AAAA,IAC/B,CAAA,SAAE;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,CAAA;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,KAAK,YAAA,EAAa;AAAA,EACpB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,UAAA,GAAa,OAAO,UAAA,KAA+B;AACvD,IAAA,IAAI,CAAC,QAAA,EAAU;AACf,IAAA,IAAI;AACF,MAAA,MAAM,IAAI,IAAA,CAAK;AAAA,QACb,SAAA,EAAW,SAAS,OAAA,CAAQ,EAAA;AAAA,QAC5B,cAAc,UAAA,CAAW,EAAA;AAAA,QACzB;AAAA,OACD,CAAA;AACD,MAAA,MAAM,YAAA,EAAa;AAAA,IACrB,SAAS,CAAA,EAAG;AACV,MAAA,QAAA,CAAU,EAAY,OAAO,CAAA;AAAA,IAC/B;AAAA,EACF,CAAA;AAEA,EAAA,uBACEA,MAAAA,CAAA,aAAA,CAAC,SAAA,EAAA,IAAA,kBACCA,MAAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAI,KAAM,CAAA,kBACXA,MAAAA,CAAA,aAAA,CAAC,YAAO,OAAA,EAAS,MAAM,KAAK,YAAA,EAAa,EAAG,QAAA,EAAU,OAAA,EAAA,EACnD,OAAA,GAAU,uBAAA,GAAW,0BACxB,CAAA,EACC,KAAA,mBAAQA,MAAAA,CAAA,cAAC,GAAA,EAAA,EAAE,KAAA,EAAO,EAAE,KAAA,EAAO,SAAA,EAAU,EAAA,EAAG,oBAAA,EAAI,KAAM,CAAA,GAAO,IAAA,EAEzD,CAAC,QAAA,GAAW,IAAA,mBACXA,MAAAA,CAAA,aAAA,CAAAA,MAAAA,CAAA,QAAA,EAAA,IAAA,kBACEA,MAAAA,CAAA,aAAA,CAAC,GAAA,EAAA,IAAA,EAAE,oBAAA,EACG,QAAA,CAAS,OAAA,CAAQ,IAAA,EAAK,0BAAA,EAAK,QAAA,CAAS,OAAA,CAAQ,KAClD,mBACAA,MAAAA,CAAA,aAAA,CAAC,GAAA,EAAA,IAAA,EAAE,sCAAA,EACM,aAAA,CAAc,MAAA,EAAO,sCAAA,EAAO,QAAA,CAAS,OAAA,CAAQ,WAAA,CAAY,cAClE,CAAA,kBACAA,OAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EACE,aAAA,CAAc,GAAA,CAAI,CAAC,IAAA,qBAClBA,MAAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,GAAA,EAAK,IAAA,CAAK,EAAA,EAAA,kBACZA,OAAA,aAAA,CAAC,QAAA,EAAA,IAAA,EAAQ,IAAA,CAAK,KAAM,CAAA,EAAS,QAAA,EAAE,IAAA,CAAK,cAAA,EAAe,uBAAA,EAAO,IAAA,CAAK,SAAA,EAAU,SAAA,EAAG,GAAA,kBAC5EA,MAAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,KAAK,UAAA,CAAW,IAAI,CAAA,EAAA,EAAG,cAAE,CAClD,CACD,CACH,CACF,CAEJ,CAAA;AAEJ,CAAA;AAEA,IAAO,+BAAA,GAAQ;AC5Ef,IAAM,SAAA,GAA4B,CAAC,QAAA,EAAU,OAAA,EAAS,QAAQ,OAAO,CAAA;AAErE,IAAM,wBAA8D,CAAC;AAAA,EACnE,MAAA;AAAA,EACA,QAAA;AAAA,EACA,cAAA;AAAA,EACA,KAAA,GAAQ;AACV,CAAA,KAAM;AACJ,EAAA,MAAM,GAAA,GAAMC,QAAQ,MAAM,MAAA,IAAU,4BAA2B,EAAG,CAAC,MAAM,CAAC,CAAA;AAE1E,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIC,SAA0C,IAAI,CAAA;AAC9E,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAIA,QAAAA,CAA2B,EAAE,CAAA;AACvE,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIA,SAAS,KAAK,CAAA;AAClD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIA,SAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAAwB,IAAI,CAAA;AAEtD,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIA,SAAS,EAAE,CAAA;AAC/C,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIA,SAAS,EAAE,CAAA;AAC7C,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIA,SAAS,EAAE,CAAA;AAC/C,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIA,SAAuB,QAAQ,CAAA;AAE/D,EAAA,MAAM,WAAW,YAAY;AAC3B,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,CAAC,OAAA,EAAS,IAAI,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,QACxC,IAAI,WAAA,EAAY;AAAA,QAChB,GAAA,CAAI,eAAA,CAAgB,EAAE,QAAA,EAAU;AAAA,OACjC,CAAA;AACD,MAAA,WAAA,CAAY,OAAO,CAAA;AACnB,MAAA,gBAAA,CAAiB,IAAI,CAAA;AAAA,IACvB,SAAS,CAAA,EAAG;AACV,MAAA,QAAA,CAAU,EAAY,OAAO,CAAA;AAAA,IAC/B,CAAA,SAAE;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,CAAA;AAEA,EAAAC,UAAU,MAAM;AACd,IAAA,KAAK,QAAA,EAAS;AAAA,EAChB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,aAAa,YAAY;AAC7B,IAAA,IAAI,CAAC,QAAA,EAAU;AACf,IAAA,MAAM,OAAA,GAAqC;AAAA,MACzC,SAAA,EAAW,SAAS,OAAA,CAAQ,EAAA;AAAA,MAC5B,QAAA;AAAA,MACA,cAAA;AAAA,MACA,KAAA,EAAO,UAAA;AAAA,MACP,WAAA,EAAa,SAAA;AAAA,MACb,IAAA,EAAM,QAAA;AAAA,MACN,IAAA,EAAM,CAAC,KAAK,CAAA;AAAA,MACZ,OAAA,EAAS;AAAA,QACP,UAAA;AAAA,QACA,MAAA,EAAQ,UAAA,GAAa,CAAC,UAAU,CAAA,GAAI;AAAA;AACtC,KACF;AAEA,IAAA,aAAA,CAAc,IAAI,CAAA;AAClB,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,CAAI,gBAAA,CAAiB,OAAA,EAAS,KAAK,CAAA;AACzC,MAAA,aAAA,CAAc,EAAE,CAAA;AAChB,MAAA,YAAA,CAAa,EAAE,CAAA;AACf,MAAA,aAAA,CAAc,EAAE,CAAA;AAChB,MAAA,MAAM,QAAA,EAAS;AAAA,IACjB,SAAS,CAAA,EAAG;AACV,MAAA,QAAA,CAAU,EAAY,OAAO,CAAA;AAAA,IAC/B,CAAA,SAAE;AACA,MAAA,aAAA,CAAc,KAAK,CAAA;AAAA,IACrB;AAAA,EACF,CAAA;AAEA,EAAA,uBACEH,OAAA,aAAA,CAAC,SAAA,EAAA,IAAA,kBACCA,MAAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAI,KAAM,CAAA,kBACXA,OAAA,aAAA,CAAC,QAAA,EAAA,EAAO,SAAS,MAAM,KAAK,UAAS,EAAG,QAAA,EAAU,OAAA,EAAA,EAC/C,OAAA,GAAU,uBAAA,GAAW,0BACxB,GACC,KAAA,mBAAQA,OAAA,aAAA,CAAC,GAAA,EAAA,EAAE,OAAO,EAAE,KAAA,EAAO,SAAA,EAAU,EAAA,EAAG,oBAAA,EAAI,KAAM,IAAO,IAAA,kBAE1DA,MAAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACCA,OAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAG,0BAAI,CAAA,kBACRA,MAAAA,CAAA,cAAC,OAAA,EAAA,EAAM,KAAA,EAAO,YAAY,QAAA,EAAU,CAAC,MAAM,aAAA,CAAc,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA,EAAG,WAAA,EAAY,4BAAO,CAAA,kBAC7FA,OAAA,aAAA,CAAC,IAAA,EAAA,IAAG,mBACJA,MAAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAS,KAAA,EAAO,SAAA,EAAW,UAAU,CAAC,CAAA,KAAM,aAAa,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA,EAAG,WAAA,EAAY,0BAAA,EAAO,CAAA,kBAC9FA,MAAAA,CAAA,cAAC,IAAA,EAAA,IAAG,CAAA,kBACJA,MAAAA,CAAA,aAAA,CAAC,OAAA,EAAA,EAAM,OAAO,UAAA,EAAY,QAAA,EAAU,CAAC,CAAA,KAAM,aAAA,CAAc,CAAA,CAAE,OAAO,KAAK,CAAA,EAAG,aAAY,kBAAA,EAAS,CAAA,kBAC/FA,MAAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAG,CAAA,kBACJA,MAAAA,CAAA,cAAC,QAAA,EAAA,EAAO,KAAA,EAAO,UAAU,QAAA,EAAU,CAAC,MAAM,WAAA,CAAY,CAAA,CAAE,MAAA,CAAO,KAAqB,CAAA,EAAA,EACjF,SAAA,CAAU,IAAI,CAAC,IAAA,qBACdA,MAAAA,CAAA,aAAA,CAAC,YAAO,KAAA,EAAO,IAAA,EAAM,GAAA,EAAK,IAAA,EAAA,EACvB,IACH,CACD,CACH,CAAA,kBACAA,MAAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,KAAK,UAAA,EAAW,EAAG,QAAA,EAAU,UAAA,IAAc,CAAC,YAChE,UAAA,GAAa,uBAAA,GAAW,0BAC3B,CACF,CAAA,kBAEAA,MAAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACCA,MAAAA,CAAA,aAAA,CAAC,YAAG,gCAAA,EAAM,aAAA,CAAc,MAAA,EAAO,QAAC,CAAA,kBAChCA,OAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EACE,aAAA,CAAc,GAAA,CAAI,CAAC,IAAA,qBAClBA,MAAAA,CAAA,aAAA,CAAC,QAAG,GAAA,EAAK,IAAA,CAAK,MACX,IAAA,CAAK,KAAA,EAAM,0BAAA,EAAK,IAAA,CAAK,MAAA,EAAO,0BAAA,EAAK,KAAK,SAAA,EACtC,IAAA,CAAK,YAAA,GAAe,CAAA,wBAAA,EAAO,IAAA,CAAK,YAAY,KAAK,EACpD,CACD,CACH,CACF,CACF,CAAA;AAEJ,CAAA;AAEA,IAAO,6BAAA,GAAQ;AC5Gf,IAAM,uBAA4D,CAAC;AAAA,EACjE,MAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA,GAAQ;AACV,CAAA,KAAM;AACJ,EAAA,MAAM,GAAA,GAAMC,QAAQ,MAAM,MAAA,IAAU,4BAA2B,EAAG,CAAC,MAAM,CAAC,CAAA;AAE1E,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIC,SAA0C,IAAI,CAAA;AAC9E,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAIA,QAAAA,CAA2B,EAAE,CAAA;AACnE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIA,SAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAAwB,IAAI,CAAA;AACtD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIA,SAAS,EAAE,CAAA;AAEzC,EAAA,MAAM,WAAW,YAAY;AAC3B,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,CAAC,OAAA,EAAS,IAAI,CAAA,GAAI,MAAM,OAAA,CAAQ,GAAA,CAAI,CAAC,GAAA,CAAI,WAAA,EAAY,EAAG,GAAA,CAAI,eAAA,EAAiB,CAAC,CAAA;AACpF,MAAA,WAAA,CAAY,OAAO,CAAA;AACnB,MAAA,cAAA,CAAe,IAAI,CAAA;AAAA,IACrB,SAAS,CAAA,EAAG;AACV,MAAA,QAAA,CAAU,EAAY,OAAO,CAAA;AAAA,IAC/B,CAAA,SAAE;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,CAAA;AAEA,EAAAC,UAAU,MAAM;AACd,IAAA,KAAK,QAAA,EAAS;AAAA,EAChB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,MAAA,GAAS,OAAO,IAAA,EAAsB,MAAA,KAAiC;AAC3E,IAAA,IAAI;AACF,MAAA,MAAM,IAAI,gBAAA,CAAiB;AAAA,QACzB,cAAc,IAAA,CAAK,EAAA;AAAA,QACnB,UAAA,EAAY,OAAA;AAAA,QACZ,MAAA;AAAA,QACA,YAAA,EAAc,MAAA,KAAW,QAAA,GAAW,gCAAA,GAAU,KAAA;AAAA,OAC/C,CAAA;AACD,MAAA,MAAM,QAAA,EAAS;AAAA,IACjB,SAAS,CAAA,EAAG;AACV,MAAA,QAAA,CAAU,EAAY,OAAO,CAAA;AAAA,IAC/B;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,OAAO,OAAA,KAAqB;AAC/C,IAAA,IAAI,CAAC,QAAA,EAAU;AACf,IAAA,IAAI;AACF,MAAA,MAAM,IAAI,mBAAA,CAAoB;AAAA,QAC5B,OAAA,EAAS;AAAA,UACP,GAAG,SAAS,OAAA,CAAQ,OAAA;AAAA,UACpB,aAAA,EAAe;AAAA;AACjB,OACD,CAAA;AACD,MAAA,MAAM,QAAA,EAAS;AAAA,IACjB,SAAS,CAAA,EAAG;AACV,MAAA,QAAA,CAAU,EAAY,OAAO,CAAA;AAAA,IAC/B;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,cAAA,GAAiB,OAAO,MAAA,KAAoB;AAChD,IAAA,IAAI,CAAC,QAAA,IAAY,CAAC,OAAA,CAAQ,MAAK,EAAG;AAClC,IAAA,IAAI;AACF,MAAA,MAAM,IAAI,mBAAA,CAAoB;AAAA,QAC5B,OAAA,EAAS,QAAQ,IAAA,EAAK;AAAA,QACtB,MAAA;AAAA,QACA,MAAA,EAAQ,SAAS,4CAAA,GAAY,4CAAA;AAAA,QAC7B,UAAA,EAAY;AAAA,OACb,CAAA;AACD,MAAA,UAAA,CAAW,EAAE,CAAA;AAAA,IACf,SAAS,CAAA,EAAG;AACV,MAAA,QAAA,CAAU,EAAY,OAAO,CAAA;AAAA,IAC/B;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,oBAAoB,YAAY;AACpC,IAAA,IAAI,CAAC,OAAA,CAAQ,IAAA,EAAK,EAAG;AACrB,IAAA,IAAI;AACF,MAAA,MAAM,IAAI,UAAA,CAAW,EAAE,SAAS,OAAA,CAAQ,IAAA,IAAQ,CAAA;AAChD,MAAA,UAAA,CAAW,EAAE,CAAA;AACb,MAAA,MAAM,QAAA,EAAS;AAAA,IACjB,SAAS,CAAA,EAAG;AACV,MAAA,QAAA,CAAU,EAAY,OAAO,CAAA;AAAA,IAC/B;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,cAAc,YAAY;AAC9B,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,iBAAA,EAAkB;AACzC,MAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,CAAC,IAAI,CAAA,EAAG;AAAA,QAC5B,IAAA,EAAM;AAAA,OACP,CAAA;AACD,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,eAAA,CAAgB,IAAI,CAAA;AACpC,MAAA,MAAM,CAAA,GAAI,QAAA,CAAS,aAAA,CAAc,GAAG,CAAA;AACpC,MAAA,CAAA,CAAE,IAAA,GAAO,GAAA;AACT,MAAA,CAAA,CAAE,QAAA,GAAW,uBAAA;AACb,MAAA,CAAA,CAAE,KAAA,EAAM;AACR,MAAA,GAAA,CAAI,gBAAgB,GAAG,CAAA;AAAA,IACzB,SAAS,CAAA,EAAG;AACV,MAAA,QAAA,CAAU,EAAY,OAAO,CAAA;AAAA,IAC/B;AAAA,EACF,CAAA;AAEA,EAAA,uBACEH,MAAAA,CAAA,aAAA,CAAC,SAAA,EAAA,IAAA,kBACCA,MAAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAI,KAAM,CAAA,kBACXA,MAAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,KAAK,QAAA,EAAS,EAAG,QAAA,EAAU,OAAA,EAAA,EAC/C,OAAA,GAAU,uBAAA,GAAW,0BACxB,CAAA,EACC,KAAA,mBAAQA,MAAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAE,KAAA,EAAO,EAAE,KAAA,EAAO,SAAA,EAAU,EAAA,EAAG,oBAAA,EAAI,KAAM,CAAA,GAAO,IAAA,kBAE1DA,MAAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACCA,MAAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAG,0BAAI,CAAA,kBACRA,MAAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,KAAK,YAAA,CAAa,IAAI,CAAA,EAAG,QAAA,EAAU,CAAC,QAAA,EAAA,EAAU,0BAErE,CAAA,kBACAA,MAAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,KAAK,YAAA,CAAa,KAAK,CAAA,EAAG,QAAA,EAAU,CAAC,QAAA,EAAA,EAAU,0BAEtE,CACF,CAAA,kBAEAA,MAAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACCA,MAAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAG,gCAAA,EAAM,WAAA,CAAY,MAAA,EAAO,QAAC,CAAA,kBAC9BA,MAAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EACE,WAAA,CAAY,GAAA,CAAI,CAAC,IAAA,qBAChBA,MAAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,GAAA,EAAK,IAAA,CAAK,EAAA,EAAA,kBACZA,MAAAA,CAAA,aAAA,CAAC,QAAA,EAAA,IAAA,EAAQ,IAAA,CAAK,KAAM,CAAA,EAAS,0BAAA,EAAK,IAAA,CAAK,cAAA,EAAe,0BAAA,EAAK,IAAA,CAAK,MAAA,EAAO,0BAAA,EAAK,IAAA,CAAK,SAAA,EAChF,KAAK,MAAA,KAAW,SAAA,mBACfA,MAAAA,CAAA,aAAA,CAAAA,MAAAA,CAAA,QAAA,EAAA,IAAA,EACG,GAAA,kBACDA,MAAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,KAAK,MAAA,CAAO,IAAA,EAAM,SAAS,CAAA,EAAA,EAAG,cAAE,CAAA,kBACvDA,MAAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,KAAK,MAAA,CAAO,IAAA,EAAM,QAAQ,CAAA,EAAA,EAAG,cAAE,CACxD,CAAA,GACE,IACN,CACD,CACH,CACF,CAAA,kBAEAA,MAAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACCA,MAAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAG,0BAAI,CAAA,kBACRA,MAAAA,CAAA,aAAA,CAAC,OAAA,EAAA,EAAM,KAAA,EAAO,OAAA,EAAS,QAAA,EAAU,CAAC,CAAA,KAAM,UAAA,CAAW,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA,EAAG,aAAY,SAAA,EAAU,CAAA,kBAC1FA,MAAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,KAAK,cAAA,CAAe,IAAI,CAAA,EAAG,QAAA,EAAU,CAAC,QAAA,EAAA,EAAU,0BAEvE,CAAA,kBACAA,MAAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,KAAK,cAAA,CAAe,KAAK,CAAA,EAAG,QAAA,EAAU,CAAC,QAAA,EAAA,EAAU,0BAExE,CAAA,kBACAA,MAAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,KAAK,iBAAA,EAAkB,EAAA,EAAG,4CAAO,CAC1D,CAAA,kBAEAA,MAAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACCA,MAAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAG,cAAE,CAAA,kBACNA,MAAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,KAAK,WAAA,EAAY,EAAA,EAAG,gCAAU,CACvD,CACF,CAAA;AAEJ,CAAA;AAEA,IAAO,4BAAA,GAAQ;;;ACpKf,IAAM,kBAAkD,CAAC;AAAA,EACvD,WAAA,GAAc,UAAA;AAAA,EACd,aAAA,GAAgB,aAAA;AAAA,EAChB,QAAA,GAAW,aAAA;AAAA,EACX,cAAA,GAAiB,mBAAA;AAAA,EACjB,OAAA,GAAU;AACZ,CAAA,KAAM;AACJ,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIE,SAA0B,WAAW,CAAA;AAE7D,EAAA,uBACEF,MAAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACCA,OAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAG,cAAY,CAAA,kBAChBA,MAAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACCA,OAAA,aAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,QAAQ,UAAU,CAAA,EAAA,EAAG,oBAAG,CAAA,kBAC/CA,MAAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,OAAA,CAAQ,QAAQ,KAAG,oBAAG,CAAA,kBAC7CA,MAAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,OAAA,CAAQ,OAAO,CAAA,EAAA,EAAG,0BAAI,CAC/C,CAAA,EAEC,IAAA,KAAS,UAAA,mBAAaA,OAAA,aAAA,CAAC,+BAAA,EAAA,EAAwB,OAAA,EAAS,aAAA,EAAe,IAAK,IAAA,EAC5E,IAAA,KAAS,QAAA,mBAAWA,OAAA,aAAA,CAAC,6BAAA,EAAA,EAAsB,QAAA,EAAU,QAAA,EAAU,gBAAgB,cAAA,EAAgB,CAAA,GAAK,IAAA,EACpG,IAAA,KAAS,0BAAUA,MAAAA,CAAA,cAAC,4BAAA,EAAA,EAAqB,OAAA,EAAkB,IAAK,IACnE,CAAA;AAEJ,CAAA;AAEA,IAAO,uBAAA,GAAQ","file":"index.mjs","sourcesContent":["import React from 'react';\nimport type { MikuContestStateSnapshot } from '../../../types';\n\nexport interface MikuContestDashboardProps {\n snapshot: MikuContestStateSnapshot;\n}\n\nconst MikuContestDashboard: React.FC<MikuContestDashboardProps> = ({ snapshot }) => {\n return (\n <div>\n <h2>{snapshot.contest.name}</h2>\n <p>{snapshot.contest.theme}</p>\n <p>投稿数:{snapshot.submissions.length}</p>\n <p>公告数:{snapshot.announcements.length}</p>\n <ul>\n {snapshot.leaderboard.map((item) => (\n <li key={item.submissionId}>\n #{item.rank} {item.title} - {item.voteCount}票\n </li>\n ))}\n </ul>\n </div>\n );\n};\n\nexport default MikuContestDashboard;\n","import type {\n CreateMikuSubmissionInput,\n MikuContestConfig,\n MikuContestStateSnapshot,\n MikuSubmission,\n MikuSubmissionFilter,\n MikuVoterRestriction,\n ResetMikuVotesInput,\n ReviewMikuSubmissionInput,\n SetMikuVoterRestrictionInput,\n VoteMikuSubmissionInput,\n} from '../../types';\n\nexport interface MikuContestApiClient {\n getSnapshot(): Promise<MikuContestStateSnapshot>;\n updateContestConfig(patch: Partial<MikuContestConfig>): Promise<MikuContestConfig>;\n createSubmission(input: CreateMikuSubmissionInput, mode?: 'web' | 'miniapp'): Promise<MikuSubmission>;\n listSubmissions(filter?: MikuSubmissionFilter): Promise<MikuSubmission[]>;\n reviewSubmission(input: ReviewMikuSubmissionInput): Promise<MikuSubmission>;\n vote(input: VoteMikuSubmissionInput): Promise<MikuSubmission>;\n setVoterRestriction(input: SetMikuVoterRestrictionInput): Promise<MikuVoterRestriction>;\n resetVotes(input: ResetMikuVotesInput): Promise<{ removedVotes: number; affectedSubmissions: string[] }>;\n exportSubmissions(filter?: MikuSubmissionFilter): Promise<ArrayBuffer>;\n}\n\nexport type HttpMethod = 'GET' | 'POST' | 'PATCH';\n\nexport interface Requester {\n <T>(url: string, options?: { method?: HttpMethod; body?: unknown }): Promise<T>;\n}\n\nconst toQueryString = (filter?: MikuSubmissionFilter): string => {\n if (!filter) return '';\n const params = new URLSearchParams();\n if (filter.status) params.set('status', filter.status);\n if (filter.type) params.set('type', filter.type);\n if (filter.authorId) params.set('authorId', filter.authorId);\n if (filter.authorKeyword) params.set('authorKeyword', filter.authorKeyword);\n if (filter.titleKeyword) params.set('titleKeyword', filter.titleKeyword);\n const query = params.toString();\n return query ? `?${query}` : '';\n};\n\ninterface ApiEnvelope<T> {\n success: boolean;\n data?: T;\n error?: string;\n}\n\nconst unwrap = <T>(result: ApiEnvelope<T>): T => {\n if (!result.success || result.data === undefined) {\n throw new Error(result.error || '请求失败');\n }\n return result.data;\n};\n\nexport const createMikuContestApiClient = (\n basePath: string,\n requester: Requester,\n): MikuContestApiClient => {\n return {\n async getSnapshot() {\n const result = await requester<ApiEnvelope<MikuContestStateSnapshot>>(`${basePath}/contest`, { method: 'GET' });\n return unwrap(result);\n },\n async updateContestConfig(patch) {\n const result = await requester<ApiEnvelope<MikuContestConfig>>(`${basePath}/contest`, {\n method: 'PATCH',\n body: patch,\n });\n return unwrap(result);\n },\n async createSubmission(input, mode = 'web') {\n const result = await requester<ApiEnvelope<MikuSubmission>>(`${basePath}/submissions`, {\n method: 'POST',\n body: { payload: input, mode },\n });\n return unwrap(result);\n },\n async listSubmissions(filter) {\n const result = await requester<ApiEnvelope<MikuSubmission[]>>(\n `${basePath}/submissions${toQueryString(filter)}`,\n { method: 'GET' },\n );\n return unwrap(result);\n },\n async reviewSubmission(input) {\n const result = await requester<ApiEnvelope<MikuSubmission>>(`${basePath}/submissions/review`, {\n method: 'POST',\n body: input,\n });\n return unwrap(result);\n },\n async vote(input) {\n const result = await requester<ApiEnvelope<MikuSubmission>>(`${basePath}/votes`, {\n method: 'POST',\n body: input,\n });\n return unwrap(result);\n },\n async setVoterRestriction(input) {\n const result = await requester<ApiEnvelope<MikuVoterRestriction>>(`${basePath}/admin/voter-restrictions`, {\n method: 'POST',\n body: input,\n });\n return unwrap(result);\n },\n async resetVotes(input) {\n const result = await requester<ApiEnvelope<{ removedVotes: number; affectedSubmissions: string[] }>>(\n `${basePath}/admin/votes/reset`,\n {\n method: 'POST',\n body: input,\n },\n );\n return unwrap(result);\n },\n async exportSubmissions(filter) {\n const response = await fetch(`${basePath}/admin/submissions/export${toQueryString(filter)}`);\n if (!response.ok) {\n throw new Error(`导出失败: ${response.status}`);\n }\n return response.arrayBuffer();\n },\n };\n};\n","import { createMikuContestApiClient, type Requester } from '../api';\n\nexport interface MikuContestWebClientOptions {\n baseUrl?: string;\n basePath?: string;\n headers?: Record<string, string>;\n}\n\nconst defaultRequester = (options: MikuContestWebClientOptions): Requester => {\n const baseUrl = options.baseUrl || '';\n const commonHeaders = options.headers || {};\n\n return async <T>(url: string, requestOptions?: { method?: 'GET' | 'POST' | 'PATCH'; body?: unknown }): Promise<T> => {\n const response = await fetch(`${baseUrl}${url}`, {\n method: requestOptions?.method || 'GET',\n headers: {\n 'Content-Type': 'application/json',\n ...commonHeaders,\n },\n body: requestOptions?.body ? JSON.stringify(requestOptions.body) : undefined,\n });\n\n const json = (await response.json()) as T;\n return json;\n };\n};\n\nexport const createMikuContestWebClient = (options: MikuContestWebClientOptions = {}) => {\n const basePath = options.basePath || '/api/miku-contest';\n return createMikuContestApiClient(basePath, defaultRequester(options));\n};\n","import React, { useEffect, useMemo, useState } from 'react';\nimport { createMikuContestWebClient } from '../../../service/web';\nimport type { MikuContestApiClient } from '../../../service/api';\nimport type { MikuContestStateSnapshot, MikuSubmission } from '../../../types';\n\nexport interface MikuContestAudiencePageProps {\n client?: Pick<MikuContestApiClient, 'getSnapshot' | 'vote'>;\n voterId: string;\n title?: string;\n}\n\nconst MikuContestAudiencePage: React.FC<MikuContestAudiencePageProps> = ({\n client,\n voterId,\n title = '观众投票区',\n}) => {\n const api = useMemo(() => client || createMikuContestWebClient(), [client]);\n\n const [snapshot, setSnapshot] = useState<MikuContestStateSnapshot | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const approvedWorks = useMemo(() => {\n if (!snapshot) return [];\n return snapshot.submissions.filter((item) => item.status === 'approved');\n }, [snapshot]);\n\n const loadSnapshot = async () => {\n setLoading(true);\n setError(null);\n try {\n const data = await api.getSnapshot();\n setSnapshot(data);\n } catch (e) {\n setError((e as Error).message);\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadSnapshot();\n }, []);\n\n const handleVote = async (submission: MikuSubmission) => {\n if (!snapshot) return;\n try {\n await api.vote({\n contestId: snapshot.contest.id,\n submissionId: submission.id,\n voterId,\n });\n await loadSnapshot();\n } catch (e) {\n setError((e as Error).message);\n }\n };\n\n return (\n <section>\n <h2>{title}</h2>\n <button onClick={() => void loadSnapshot()} disabled={loading}>\n {loading ? '刷新中...' : '刷新数据'}\n </button>\n {error ? <p style={{ color: 'crimson' }}>错误:{error}</p> : null}\n\n {!snapshot ? null : (\n <>\n <p>\n 赛事:{snapshot.contest.name}|主题:{snapshot.contest.theme}\n </p>\n <p>\n 已过审作品:{approvedWorks.length}|每日上限:{snapshot.contest.votingRules.maxVotesPerDay}\n </p>\n <ul>\n {approvedWorks.map((work) => (\n <li key={work.id}>\n <strong>{work.title}</strong>({work.authorNickname})- 当前 {work.voteCount} 票{' '}\n <button onClick={() => void handleVote(work)}>投票</button>\n </li>\n ))}\n </ul>\n </>\n )}\n </section>\n );\n};\n\nexport default MikuContestAudiencePage;\n","import React, { useEffect, useMemo, useState } from 'react';\nimport { createMikuContestWebClient } from '../../../service/web';\nimport type { MikuContestApiClient } from '../../../service/api';\nimport type { CreateMikuSubmissionInput, MikuContestStateSnapshot, MikuSubmission, MikuWorkType } from '../../../types';\n\nexport interface MikuContestArtistPageProps {\n client?: Pick<MikuContestApiClient, 'getSnapshot' | 'createSubmission' | 'listSubmissions'>;\n authorId: string;\n authorNickname: string;\n title?: string;\n}\n\nconst workTypes: MikuWorkType[] = ['visual', 'video', 'text', 'audio'];\n\nconst MikuContestArtistPage: React.FC<MikuContestArtistPageProps> = ({\n client,\n authorId,\n authorNickname,\n title = '画师投稿区',\n}) => {\n const api = useMemo(() => client || createMikuContestWebClient(), [client]);\n\n const [snapshot, setSnapshot] = useState<MikuContestStateSnapshot | null>(null);\n const [mySubmissions, setMySubmissions] = useState<MikuSubmission[]>([]);\n const [submitting, setSubmitting] = useState(false);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const [titleInput, setTitleInput] = useState('');\n const [descInput, setDescInput] = useState('');\n const [coverImage, setCoverImage] = useState('');\n const [workType, setWorkType] = useState<MikuWorkType>('visual');\n\n const loadData = async () => {\n setLoading(true);\n setError(null);\n try {\n const [contest, mine] = await Promise.all([\n api.getSnapshot(),\n api.listSubmissions({ authorId }),\n ]);\n setSnapshot(contest);\n setMySubmissions(mine);\n } catch (e) {\n setError((e as Error).message);\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, []);\n\n const submitWork = async () => {\n if (!snapshot) return;\n const payload: CreateMikuSubmissionInput = {\n contestId: snapshot.contest.id,\n authorId,\n authorNickname,\n title: titleInput,\n description: descInput,\n type: workType,\n tags: ['web'],\n content: {\n coverImage,\n images: coverImage ? [coverImage] : undefined,\n },\n };\n\n setSubmitting(true);\n setError(null);\n try {\n await api.createSubmission(payload, 'web');\n setTitleInput('');\n setDescInput('');\n setCoverImage('');\n await loadData();\n } catch (e) {\n setError((e as Error).message);\n } finally {\n setSubmitting(false);\n }\n };\n\n return (\n <section>\n <h2>{title}</h2>\n <button onClick={() => void loadData()} disabled={loading}>\n {loading ? '刷新中...' : '刷新数据'}\n </button>\n {error ? <p style={{ color: 'crimson' }}>错误:{error}</p> : null}\n\n <div>\n <h3>新建投稿</h3>\n <input value={titleInput} onChange={(e) => setTitleInput(e.target.value)} placeholder=\"作品标题\" />\n <br />\n <textarea value={descInput} onChange={(e) => setDescInput(e.target.value)} placeholder=\"作品简介\" />\n <br />\n <input value={coverImage} onChange={(e) => setCoverImage(e.target.value)} placeholder=\"封面 URL\" />\n <br />\n <select value={workType} onChange={(e) => setWorkType(e.target.value as MikuWorkType)}>\n {workTypes.map((item) => (\n <option value={item} key={item}>\n {item}\n </option>\n ))}\n </select>\n <button onClick={() => void submitWork()} disabled={submitting || !snapshot}>\n {submitting ? '提交中...' : '提交稿件'}\n </button>\n </div>\n\n <div>\n <h3>我的投稿({mySubmissions.length})</h3>\n <ul>\n {mySubmissions.map((item) => (\n <li key={item.id}>\n {item.title}|状态:{item.status}|票数:{item.voteCount}\n {item.rejectReason ? `|驳回:${item.rejectReason}` : ''}\n </li>\n ))}\n </ul>\n </div>\n </section>\n );\n};\n\nexport default MikuContestArtistPage;\n","import React, { useEffect, useMemo, useState } from 'react';\nimport { createMikuContestWebClient } from '../../../service/web';\nimport type { MikuContestApiClient } from '../../../service/api';\nimport type { MikuContestStateSnapshot, MikuSubmission } from '../../../types';\n\nexport interface MikuContestAdminPageProps {\n client?: Pick<\n MikuContestApiClient,\n | 'getSnapshot'\n | 'listSubmissions'\n | 'reviewSubmission'\n | 'setVoterRestriction'\n | 'resetVotes'\n | 'exportSubmissions'\n | 'updateContestConfig'\n >;\n adminId: string;\n title?: string;\n}\n\nconst MikuContestAdminPage: React.FC<MikuContestAdminPageProps> = ({\n client,\n adminId,\n title = '管理员面板',\n}) => {\n const api = useMemo(() => client || createMikuContestWebClient(), [client]);\n\n const [snapshot, setSnapshot] = useState<MikuContestStateSnapshot | null>(null);\n const [submissions, setSubmissions] = useState<MikuSubmission[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [voterId, setVoterId] = useState('');\n\n const loadData = async () => {\n setLoading(true);\n setError(null);\n try {\n const [contest, list] = await Promise.all([api.getSnapshot(), api.listSubmissions()]);\n setSnapshot(contest);\n setSubmissions(list);\n } catch (e) {\n setError((e as Error).message);\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, []);\n\n const review = async (item: MikuSubmission, action: 'approve' | 'reject') => {\n try {\n await api.reviewSubmission({\n submissionId: item.id,\n reviewerId: adminId,\n action,\n rejectReason: action === 'reject' ? '管理员驳回' : undefined,\n });\n await loadData();\n } catch (e) {\n setError((e as Error).message);\n }\n };\n\n const toggleVoting = async (enabled: boolean) => {\n if (!snapshot) return;\n try {\n await api.updateContestConfig({\n toggles: {\n ...snapshot.contest.toggles,\n votingEnabled: enabled,\n },\n });\n await loadData();\n } catch (e) {\n setError((e as Error).message);\n }\n };\n\n const setRestriction = async (banned: boolean) => {\n if (!snapshot || !voterId.trim()) return;\n try {\n await api.setVoterRestriction({\n voterId: voterId.trim(),\n banned,\n reason: banned ? '管理员手动封禁' : '管理员解除封禁',\n operatorId: adminId,\n });\n setVoterId('');\n } catch (e) {\n setError((e as Error).message);\n }\n };\n\n const resetVotesByVoter = async () => {\n if (!voterId.trim()) return;\n try {\n await api.resetVotes({ voterId: voterId.trim() });\n setVoterId('');\n await loadData();\n } catch (e) {\n setError((e as Error).message);\n }\n };\n\n const exportExcel = async () => {\n try {\n const data = await api.exportSubmissions();\n const blob = new Blob([data], {\n type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'miku-submissions.xlsx';\n a.click();\n URL.revokeObjectURL(url);\n } catch (e) {\n setError((e as Error).message);\n }\n };\n\n return (\n <section>\n <h2>{title}</h2>\n <button onClick={() => void loadData()} disabled={loading}>\n {loading ? '刷新中...' : '刷新数据'}\n </button>\n {error ? <p style={{ color: 'crimson' }}>错误:{error}</p> : null}\n\n <div>\n <h3>赛事开关</h3>\n <button onClick={() => void toggleVoting(true)} disabled={!snapshot}>\n 开启投票\n </button>\n <button onClick={() => void toggleVoting(false)} disabled={!snapshot}>\n 关闭投票\n </button>\n </div>\n\n <div>\n <h3>投稿审核({submissions.length})</h3>\n <ul>\n {submissions.map((item) => (\n <li key={item.id}>\n <strong>{item.title}</strong>|作者:{item.authorNickname}|状态:{item.status}|票数:{item.voteCount}\n {item.status === 'pending' ? (\n <>\n {' '}\n <button onClick={() => void review(item, 'approve')}>通过</button>\n <button onClick={() => void review(item, 'reject')}>驳回</button>\n </>\n ) : null}\n </li>\n ))}\n </ul>\n </div>\n\n <div>\n <h3>投票风控</h3>\n <input value={voterId} onChange={(e) => setVoterId(e.target.value)} placeholder=\"voterId\" />\n <button onClick={() => void setRestriction(true)} disabled={!snapshot}>\n 封禁投票\n </button>\n <button onClick={() => void setRestriction(false)} disabled={!snapshot}>\n 解除封禁\n </button>\n <button onClick={() => void resetVotesByVoter()}>清零该用户票数</button>\n </div>\n\n <div>\n <h3>导出</h3>\n <button onClick={() => void exportExcel()}>导出投稿 Excel</button>\n </div>\n </section>\n );\n};\n\nexport default MikuContestAdminPage;\n","import React, { useState } from 'react';\nimport MikuContestAudiencePage from './MikuContestAudiencePage';\nimport MikuContestArtistPage from './MikuContestArtistPage';\nimport MikuContestAdminPage from './MikuContestAdminPage';\n\ntype MikuContestView = 'audience' | 'artist' | 'admin';\n\nexport interface MikuContestPageProps {\n defaultView?: MikuContestView;\n viewerVoterId?: string;\n artistId?: string;\n artistNickname?: string;\n adminId?: string;\n}\n\nconst MikuContestPage: React.FC<MikuContestPageProps> = ({\n defaultView = 'audience',\n viewerVoterId = 'viewer-demo',\n artistId = 'artist-demo',\n artistNickname = 'Demo 画师',\n adminId = 'admin-demo',\n}) => {\n const [view, setView] = useState<MikuContestView>(defaultView);\n\n return (\n <div>\n <h1>Miku Contest</h1>\n <div>\n <button onClick={() => setView('audience')}>观众端</button>\n <button onClick={() => setView('artist')}>画师端</button>\n <button onClick={() => setView('admin')}>管理员端</button>\n </div>\n\n {view === 'audience' ? <MikuContestAudiencePage voterId={viewerVoterId} /> : null}\n {view === 'artist' ? <MikuContestArtistPage authorId={artistId} authorNickname={artistNickname} /> : null}\n {view === 'admin' ? <MikuContestAdminPage adminId={adminId} /> : null}\n </div>\n );\n};\n\nexport default MikuContestPage;\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { MikuContestConfig, CreateMikuSubmissionInput, MikuSubmission, MikuSubmissionFilter, ReviewMikuSubmissionInput, VoteMikuSubmissionInput, MikuVoterRestriction, SetMikuVoterRestrictionInput, ResetMikuVotesInput, MikuAnnouncement, CreateMikuAnnouncementInput, MikuLeaderboardItem, MikuContestStateSnapshot, MikuSubmissionExportRow, MikuContestPersistedState } from './mikuContest/types/index.js';
|
|
2
|
+
|
|
3
|
+
interface MikuContestServiceOptions {
|
|
4
|
+
contestConfig?: Partial<MikuContestConfig>;
|
|
5
|
+
}
|
|
6
|
+
declare class MikuContestService {
|
|
7
|
+
private contest;
|
|
8
|
+
private readonly submissions;
|
|
9
|
+
private readonly votes;
|
|
10
|
+
private readonly announcements;
|
|
11
|
+
private readonly voterRestrictions;
|
|
12
|
+
constructor(options?: MikuContestServiceOptions);
|
|
13
|
+
getContestConfig(): MikuContestConfig;
|
|
14
|
+
updateContestConfig(patch: Partial<MikuContestConfig>): MikuContestConfig;
|
|
15
|
+
createSubmission(input: CreateMikuSubmissionInput, mode?: 'web' | 'miniapp'): MikuSubmission;
|
|
16
|
+
listSubmissions(filter?: MikuSubmissionFilter): MikuSubmission[];
|
|
17
|
+
getSubmission(submissionId: string): MikuSubmission | null;
|
|
18
|
+
reviewSubmission(input: ReviewMikuSubmissionInput): MikuSubmission;
|
|
19
|
+
vote(input: VoteMikuSubmissionInput): MikuSubmission;
|
|
20
|
+
getVoterRestriction(voterId: string): MikuVoterRestriction | null;
|
|
21
|
+
setVoterRestriction(input: SetMikuVoterRestrictionInput): MikuVoterRestriction;
|
|
22
|
+
resetVotes(input: ResetMikuVotesInput): {
|
|
23
|
+
removedVotes: number;
|
|
24
|
+
affectedSubmissions: string[];
|
|
25
|
+
};
|
|
26
|
+
listAnnouncements(contestId?: string): MikuAnnouncement[];
|
|
27
|
+
publishAnnouncement(input: CreateMikuAnnouncementInput): MikuAnnouncement;
|
|
28
|
+
getLeaderboard(limit?: number): MikuLeaderboardItem[];
|
|
29
|
+
getSnapshot(): MikuContestStateSnapshot;
|
|
30
|
+
getSubmissionExportRows(filter?: MikuSubmissionFilter): MikuSubmissionExportRow[];
|
|
31
|
+
exportSubmissionExcel(filter?: MikuSubmissionFilter): Uint8Array;
|
|
32
|
+
private recalculateVoteCounts;
|
|
33
|
+
exportPersistenceState(): MikuContestPersistedState;
|
|
34
|
+
importPersistenceState(state: MikuContestPersistedState): void;
|
|
35
|
+
}
|
|
36
|
+
declare const createMikuContestService: (options?: MikuContestServiceOptions) => MikuContestService;
|
|
37
|
+
|
|
38
|
+
export { type MikuContestServiceOptions as M, MikuContestService as a, createMikuContestService as c };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { MikuContestConfig, CreateMikuSubmissionInput, MikuSubmission, MikuSubmissionFilter, ReviewMikuSubmissionInput, VoteMikuSubmissionInput, MikuVoterRestriction, SetMikuVoterRestrictionInput, ResetMikuVotesInput, MikuAnnouncement, CreateMikuAnnouncementInput, MikuLeaderboardItem, MikuContestStateSnapshot, MikuSubmissionExportRow, MikuContestPersistedState } from './mikuContest/types/index.mjs';
|
|
2
|
+
|
|
3
|
+
interface MikuContestServiceOptions {
|
|
4
|
+
contestConfig?: Partial<MikuContestConfig>;
|
|
5
|
+
}
|
|
6
|
+
declare class MikuContestService {
|
|
7
|
+
private contest;
|
|
8
|
+
private readonly submissions;
|
|
9
|
+
private readonly votes;
|
|
10
|
+
private readonly announcements;
|
|
11
|
+
private readonly voterRestrictions;
|
|
12
|
+
constructor(options?: MikuContestServiceOptions);
|
|
13
|
+
getContestConfig(): MikuContestConfig;
|
|
14
|
+
updateContestConfig(patch: Partial<MikuContestConfig>): MikuContestConfig;
|
|
15
|
+
createSubmission(input: CreateMikuSubmissionInput, mode?: 'web' | 'miniapp'): MikuSubmission;
|
|
16
|
+
listSubmissions(filter?: MikuSubmissionFilter): MikuSubmission[];
|
|
17
|
+
getSubmission(submissionId: string): MikuSubmission | null;
|
|
18
|
+
reviewSubmission(input: ReviewMikuSubmissionInput): MikuSubmission;
|
|
19
|
+
vote(input: VoteMikuSubmissionInput): MikuSubmission;
|
|
20
|
+
getVoterRestriction(voterId: string): MikuVoterRestriction | null;
|
|
21
|
+
setVoterRestriction(input: SetMikuVoterRestrictionInput): MikuVoterRestriction;
|
|
22
|
+
resetVotes(input: ResetMikuVotesInput): {
|
|
23
|
+
removedVotes: number;
|
|
24
|
+
affectedSubmissions: string[];
|
|
25
|
+
};
|
|
26
|
+
listAnnouncements(contestId?: string): MikuAnnouncement[];
|
|
27
|
+
publishAnnouncement(input: CreateMikuAnnouncementInput): MikuAnnouncement;
|
|
28
|
+
getLeaderboard(limit?: number): MikuLeaderboardItem[];
|
|
29
|
+
getSnapshot(): MikuContestStateSnapshot;
|
|
30
|
+
getSubmissionExportRows(filter?: MikuSubmissionFilter): MikuSubmissionExportRow[];
|
|
31
|
+
exportSubmissionExcel(filter?: MikuSubmissionFilter): Uint8Array;
|
|
32
|
+
private recalculateVoteCounts;
|
|
33
|
+
exportPersistenceState(): MikuContestPersistedState;
|
|
34
|
+
importPersistenceState(state: MikuContestPersistedState): void;
|
|
35
|
+
}
|
|
36
|
+
declare const createMikuContestService: (options?: MikuContestServiceOptions) => MikuContestService;
|
|
37
|
+
|
|
38
|
+
export { type MikuContestServiceOptions as M, MikuContestService as a, createMikuContestService as c };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MikuContestPersistedState } from './mikuContest/types/index.mjs';
|
|
2
|
+
|
|
3
|
+
interface MikuContestPersistenceAdapter {
|
|
4
|
+
loadState(contestId: string): Promise<MikuContestPersistedState | null>;
|
|
5
|
+
saveState(state: MikuContestPersistedState): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
type DrizzleLikeDb = {
|
|
8
|
+
select: (...args: any[]) => any;
|
|
9
|
+
insert: (...args: any[]) => any;
|
|
10
|
+
update: (...args: any[]) => any;
|
|
11
|
+
delete: (...args: any[]) => any;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type { DrizzleLikeDb as D, MikuContestPersistenceAdapter as M };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MikuContestPersistedState } from './mikuContest/types/index.js';
|
|
2
|
+
|
|
3
|
+
interface MikuContestPersistenceAdapter {
|
|
4
|
+
loadState(contestId: string): Promise<MikuContestPersistedState | null>;
|
|
5
|
+
saveState(state: MikuContestPersistedState): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
type DrizzleLikeDb = {
|
|
8
|
+
select: (...args: any[]) => any;
|
|
9
|
+
insert: (...args: any[]) => any;
|
|
10
|
+
update: (...args: any[]) => any;
|
|
11
|
+
delete: (...args: any[]) => any;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type { DrizzleLikeDb as D, MikuContestPersistenceAdapter as M };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sa2kit",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.92",
|
|
4
4
|
"description": "A modern, type-safe React utility library with cross-platform support and platform adapters",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -398,6 +398,46 @@
|
|
|
398
398
|
"import": "./dist/vocaloidBooth/server/index.mjs",
|
|
399
399
|
"require": "./dist/vocaloidBooth/server/index.js"
|
|
400
400
|
},
|
|
401
|
+
"./mikuContest": {
|
|
402
|
+
"types": "./dist/mikuContest/index.d.ts",
|
|
403
|
+
"import": "./dist/mikuContest/index.mjs",
|
|
404
|
+
"require": "./dist/mikuContest/index.js"
|
|
405
|
+
},
|
|
406
|
+
"./mikuContest/types": {
|
|
407
|
+
"types": "./dist/mikuContest/types/index.d.ts",
|
|
408
|
+
"import": "./dist/mikuContest/types/index.mjs",
|
|
409
|
+
"require": "./dist/mikuContest/types/index.js"
|
|
410
|
+
},
|
|
411
|
+
"./mikuContest/logic": {
|
|
412
|
+
"types": "./dist/mikuContest/logic/index.d.ts",
|
|
413
|
+
"import": "./dist/mikuContest/logic/index.mjs",
|
|
414
|
+
"require": "./dist/mikuContest/logic/index.js"
|
|
415
|
+
},
|
|
416
|
+
"./mikuContest/service": {
|
|
417
|
+
"types": "./dist/mikuContest/service/index.d.ts",
|
|
418
|
+
"import": "./dist/mikuContest/service/index.mjs",
|
|
419
|
+
"require": "./dist/mikuContest/service/index.js"
|
|
420
|
+
},
|
|
421
|
+
"./mikuContest/ui/web": {
|
|
422
|
+
"types": "./dist/mikuContest/ui/web/index.d.ts",
|
|
423
|
+
"import": "./dist/mikuContest/ui/web/index.mjs",
|
|
424
|
+
"require": "./dist/mikuContest/ui/web/index.js"
|
|
425
|
+
},
|
|
426
|
+
"./mikuContest/ui/miniapp": {
|
|
427
|
+
"types": "./dist/mikuContest/ui/miniapp/index.d.ts",
|
|
428
|
+
"import": "./dist/mikuContest/ui/miniapp/index.mjs",
|
|
429
|
+
"require": "./dist/mikuContest/ui/miniapp/index.js"
|
|
430
|
+
},
|
|
431
|
+
"./mikuContest/server": {
|
|
432
|
+
"types": "./dist/mikuContest/server/index.d.ts",
|
|
433
|
+
"import": "./dist/mikuContest/server/index.mjs",
|
|
434
|
+
"require": "./dist/mikuContest/server/index.js"
|
|
435
|
+
},
|
|
436
|
+
"./mikuContest/routes": {
|
|
437
|
+
"types": "./dist/mikuContest/routes/index.d.ts",
|
|
438
|
+
"import": "./dist/mikuContest/routes/index.mjs",
|
|
439
|
+
"require": "./dist/mikuContest/routes/index.js"
|
|
440
|
+
},
|
|
401
441
|
"./components": {
|
|
402
442
|
"types": "./dist/components/index.d.ts",
|
|
403
443
|
"import": "./dist/components/index.mjs",
|