@xcpcio/board-app 0.29.0 → 0.31.0
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/assets/Board-3c31679a.css +1 -0
- package/dist/assets/Board-ca17ed93.js +3 -0
- package/dist/assets/DataSourceInput.vue_vue_type_script_setup_true_lang-39c4cbe1.js +1 -0
- package/dist/assets/{TheInput.vue_vue_type_script_setup_true_lang-1dafe5cb.js → TheInput.vue_vue_type_script_setup_true_lang-34252075.js} +1 -1
- package/dist/assets/{_...all_-d54370e4.js → _...all_-69526490.js} +1 -1
- package/dist/assets/{_name_-c7264d89.js → _name_-ec4f9242.js} +1 -1
- package/dist/assets/{about-55b7ba92.js → about-07cc8f15.js} +1 -1
- package/dist/assets/board-82816108.js +1 -0
- package/dist/assets/{board-layout-e4e0bae6.js → board-layout-5322ed1f.js} +1 -1
- package/dist/assets/{en-644e039f.js → en-06ebe6c4.js} +1 -1
- package/dist/assets/{headless-3466779c.js → headless-21807d6b.js} +1 -1
- package/dist/assets/{home-37e85146.js → home-93d9a706.js} +1 -1
- package/dist/assets/index-096b8dfd.js +1 -0
- package/dist/assets/{index-a0a089d2.css → index-2935d261.css} +1 -1
- package/dist/assets/index-a835bec8.js +1 -0
- package/dist/assets/{index-f1d19db1.js → index-cefad7d4.js} +43 -43
- package/dist/assets/{index-layout-c91f7cfd.js → index-layout-2da78a05.js} +1 -1
- package/dist/assets/{test-133c6df2.js → test-f6a5238f.js} +1 -1
- package/dist/assets/{useQueryBoardData-c0567554.js → useQueryBoardData-4ec8c75f.js} +1 -1
- package/dist/assets/{user-6ae7ff90.js → user-33c76882.js} +1 -1
- package/dist/assets/{virtual_pwa-register-7ea4486e.js → virtual_pwa-register-1f370632.js} +1 -1
- package/dist/assets/{zh-CN-44b801f0.js → zh-CN-582540fd.js} +1 -1
- package/dist/index.html +3 -3
- package/dist/sw.js +1 -1
- package/package.json +3 -3
- package/src/auto-imports.d.ts +3 -0
- package/src/components/Balloon.vue +2 -1
- package/src/components/Countdown.vue +104 -0
- package/src/components/CustomCountdown.vue +25 -0
- package/src/components/board/Board.vue +6 -0
- package/src/components/board/ProblemInfoModal.vue +6 -0
- package/src/components/board/SubmissionsTable.vue +260 -58
- package/src/components/board/TeamAwards.vue +49 -19
- package/src/components/board/TeamInfoModal.vue +20 -7
- package/src/components/board/Utility.vue +17 -0
- package/src/components/flowbite/Tooltip.vue +1 -1
- package/src/components.d.ts +2 -0
- package/src/composables/constant.ts +1 -0
- package/src/pages/countdown/index.vue +10 -0
- package/src/pages/index.vue +13 -6
- package/src/styles/submission-status-filter.css +123 -0
- package/dist/assets/Board-2cad79eb.css +0 -1
- package/dist/assets/Board-9442407d.js +0 -3
- package/dist/assets/DataSourceInput.vue_vue_type_script_setup_true_lang-d16f2a12.js +0 -1
- package/dist/assets/board-88a98e6a.js +0 -1
- package/dist/assets/index-bc173751.js +0 -1
|
@@ -1,23 +1,210 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import { MultiSelect } from "vue-search-select";
|
|
3
|
+
|
|
4
|
+
import type { Rank, SelectOptionItem, Submissions } from "@xcpcio/core";
|
|
3
5
|
import { Submission } from "@xcpcio/core";
|
|
6
|
+
import type { SubmissionStatus } from "@xcpcio/types";
|
|
4
7
|
import { SubmissionStatusToString } from "@xcpcio/types";
|
|
5
8
|
|
|
6
9
|
import { Pagination } from "~/composables/pagination";
|
|
7
10
|
|
|
11
|
+
import "~/styles/submission-status-filter.css";
|
|
12
|
+
|
|
13
|
+
interface FilterOptions {
|
|
14
|
+
orgNames: string[];
|
|
15
|
+
teamIds: string[];
|
|
16
|
+
languages: string[];
|
|
17
|
+
statuses: SubmissionStatus[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface EnableFilterOptions {
|
|
21
|
+
organization?: boolean,
|
|
22
|
+
team?: boolean,
|
|
23
|
+
language?: boolean,
|
|
24
|
+
status?: boolean,
|
|
25
|
+
}
|
|
26
|
+
|
|
8
27
|
const props = defineProps<{
|
|
9
28
|
rank: Rank,
|
|
10
29
|
submissions: Submissions,
|
|
11
30
|
pageSize?: number,
|
|
12
31
|
removeBorder?: boolean,
|
|
32
|
+
enableFilter?: EnableFilterOptions,
|
|
13
33
|
}>();
|
|
14
34
|
|
|
15
35
|
const rank = computed(() => props.rank);
|
|
36
|
+
const enableFilter = computed(() => props.enableFilter);
|
|
37
|
+
const enableFilterButton = computed(() => {
|
|
38
|
+
if (!enableFilter.value) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const [_k, v] of Object.entries(enableFilter.value)) {
|
|
43
|
+
if (v === true) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return false;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const filterOptions = ref<FilterOptions>({
|
|
52
|
+
orgNames: [],
|
|
53
|
+
teamIds: [],
|
|
54
|
+
languages: [],
|
|
55
|
+
statuses: [],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const orgOptions = computed(() => {
|
|
59
|
+
const res = rank.value.organizations.map((o) => {
|
|
60
|
+
return {
|
|
61
|
+
value: o,
|
|
62
|
+
text: o,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return res;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const orgSelectedItems = ref<Array<SelectOptionItem>>([]);
|
|
70
|
+
const orgLastSelectItem = ref({});
|
|
71
|
+
|
|
72
|
+
function orgOnSelect(selectedItems: Array<SelectOptionItem>, lastSelectItem: SelectOptionItem) {
|
|
73
|
+
orgSelectedItems.value = selectedItems;
|
|
74
|
+
orgLastSelectItem.value = lastSelectItem;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const teamsOptions = computed(() => {
|
|
78
|
+
const res = rank.value.originTeams.map((t) => {
|
|
79
|
+
return {
|
|
80
|
+
value: t.id,
|
|
81
|
+
text: t.organization ? `${t.name} - ${t.organization}` : t.name,
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return res;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const teamsSelectedItems = ref<Array<SelectOptionItem>>([]);
|
|
89
|
+
const teamsLastSelectItem = ref({});
|
|
90
|
+
|
|
91
|
+
function teamsOnSelect(selectedItems: Array<SelectOptionItem>, lastSelectItem: SelectOptionItem) {
|
|
92
|
+
teamsSelectedItems.value = selectedItems;
|
|
93
|
+
teamsLastSelectItem.value = lastSelectItem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const languageOptions = computed(() => {
|
|
97
|
+
const languages = rank.value.languages;
|
|
98
|
+
|
|
99
|
+
const res = languages.map((l) => {
|
|
100
|
+
return {
|
|
101
|
+
value: l,
|
|
102
|
+
text: l,
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return res;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const languageSelectedItems = ref<Array<SelectOptionItem>>([]);
|
|
110
|
+
const languageLastSelectItem = ref({});
|
|
111
|
+
|
|
112
|
+
function languageOnSelect(selectedItems: Array<SelectOptionItem>, lastSelectItem: SelectOptionItem) {
|
|
113
|
+
languageSelectedItems.value = selectedItems;
|
|
114
|
+
languageLastSelectItem.value = lastSelectItem;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const statusOptions = computed(() => {
|
|
118
|
+
const statuses = rank.value.statuses;
|
|
119
|
+
|
|
120
|
+
const res = statuses.map((s) => {
|
|
121
|
+
return {
|
|
122
|
+
value: s,
|
|
123
|
+
text: SubmissionStatusToString[s],
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return res;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const statusSelectedItems = ref<Array<SelectOptionItem>>([]);
|
|
131
|
+
const statusLastSelectItem = ref({});
|
|
132
|
+
|
|
133
|
+
function statusOnSelect(selectedItems: Array<SelectOptionItem>, lastSelectItem: SelectOptionItem) {
|
|
134
|
+
statusSelectedItems.value = selectedItems;
|
|
135
|
+
statusLastSelectItem.value = lastSelectItem;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function statusCustomAttr(option: SelectOptionItem) {
|
|
139
|
+
return option.value.toString();
|
|
140
|
+
}
|
|
141
|
+
|
|
16
142
|
const submissions = computed(() => {
|
|
17
|
-
const
|
|
18
|
-
return
|
|
143
|
+
const ss = props.submissions;
|
|
144
|
+
return ss.filter((s) => {
|
|
145
|
+
const o = filterOptions.value;
|
|
146
|
+
|
|
147
|
+
if (o.orgNames.length === 0
|
|
148
|
+
&& o.teamIds.length === 0
|
|
149
|
+
&& o.languages.length === 0
|
|
150
|
+
&& o.statuses.length === 0
|
|
151
|
+
) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (o.teamIds.length > 0) {
|
|
156
|
+
for (const t of o.teamIds) {
|
|
157
|
+
if (t === s.teamId) {
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (o.orgNames.length > 0) {
|
|
164
|
+
const team = rank.value.teamsMap.get(s.teamId);
|
|
165
|
+
for (const n of o.orgNames) {
|
|
166
|
+
if (n === team?.organization) {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (o.languages.length > 0) {
|
|
173
|
+
for (const l of o.languages) {
|
|
174
|
+
if (l === s.language) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (o.statuses.length > 0) {
|
|
181
|
+
for (const sta of o.statuses) {
|
|
182
|
+
if (sta === s.status) {
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return false;
|
|
189
|
+
}).sort(Submission.compare).reverse();
|
|
19
190
|
});
|
|
20
191
|
|
|
192
|
+
function onFilter() {
|
|
193
|
+
const newFilterOptions: FilterOptions = {
|
|
194
|
+
orgNames: [],
|
|
195
|
+
teamIds: [],
|
|
196
|
+
languages: [],
|
|
197
|
+
statuses: [],
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
newFilterOptions.orgNames = orgSelectedItems.value.map(o => o.value);
|
|
201
|
+
newFilterOptions.teamIds = teamsSelectedItems.value.map(t => t.value);
|
|
202
|
+
newFilterOptions.languages = languageSelectedItems.value.map(l => l.value);
|
|
203
|
+
newFilterOptions.statuses = statusSelectedItems.value.map(s => s.value as SubmissionStatus);
|
|
204
|
+
|
|
205
|
+
filterOptions.value = newFilterOptions;
|
|
206
|
+
}
|
|
207
|
+
|
|
21
208
|
const p = ref(new Pagination());
|
|
22
209
|
|
|
23
210
|
p.value.currentPage = 0;
|
|
@@ -69,79 +256,94 @@ function getProblemLabelColorStyle(s: Submission) {
|
|
|
69
256
|
<template>
|
|
70
257
|
<section>
|
|
71
258
|
<div
|
|
72
|
-
|
|
259
|
+
mx-auto w-full
|
|
73
260
|
px-4
|
|
74
261
|
>
|
|
75
262
|
<div
|
|
76
|
-
|
|
263
|
+
relative overflow-hidden
|
|
264
|
+
bg-white dark:bg-gray-800
|
|
77
265
|
:class="{
|
|
78
266
|
'shadow-md': props.removeBorder !== true,
|
|
79
267
|
'sm:rounded-sm': props.removeBorder !== true,
|
|
80
268
|
}"
|
|
81
269
|
>
|
|
82
270
|
<div
|
|
83
|
-
|
|
271
|
+
space-y-3
|
|
84
272
|
flex flex-col
|
|
85
273
|
px-4 py-3
|
|
274
|
+
lg:flex-row lg:items-center lg:justify-between
|
|
275
|
+
lg:space-x-4 lg:space-y-0
|
|
86
276
|
>
|
|
87
277
|
<div
|
|
88
|
-
|
|
278
|
+
flex flex-shrink-0 flex-col
|
|
279
|
+
md:flex-row md:items-center
|
|
280
|
+
lg:justify-end space-y-3
|
|
281
|
+
md:space-x-3 md:space-y-0
|
|
89
282
|
>
|
|
90
|
-
<
|
|
91
|
-
v-if="
|
|
92
|
-
|
|
93
|
-
class="flex items-center justify-center rounded-lg bg-primary-700 px-4 py-2 text-sm font-medium text-white dark:bg-primary-600 hover:bg-primary-800 focus:outline-none focus:ring-4 focus:ring-primary-300 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
|
|
283
|
+
<div
|
|
284
|
+
v-if="rank.contest.organization && enableFilter?.organization"
|
|
285
|
+
w-48
|
|
94
286
|
>
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
/>
|
|
107
|
-
</svg>
|
|
108
|
-
Add new product
|
|
109
|
-
</button>
|
|
110
|
-
|
|
111
|
-
<button
|
|
112
|
-
v-if="notShowing"
|
|
113
|
-
type="button"
|
|
114
|
-
class="flex flex-shrink-0 items-center justify-center border border-gray-200 rounded-lg bg-white px-3 py-2 text-sm font-medium text-gray-900 focus:z-10 dark:border-gray-600 dark:bg-gray-800 hover:bg-gray-100 dark:text-gray-400 hover:text-primary-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
|
|
287
|
+
<MultiSelect
|
|
288
|
+
:options="orgOptions"
|
|
289
|
+
:selected-options="orgSelectedItems"
|
|
290
|
+
:placeholder="rank.contest.organization"
|
|
291
|
+
@select="orgOnSelect"
|
|
292
|
+
/>
|
|
293
|
+
</div>
|
|
294
|
+
|
|
295
|
+
<div
|
|
296
|
+
v-if="enableFilter?.team"
|
|
297
|
+
w-48
|
|
115
298
|
>
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
299
|
+
<MultiSelect
|
|
300
|
+
:options="teamsOptions"
|
|
301
|
+
:selected-options="teamsSelectedItems"
|
|
302
|
+
placeholder="Team"
|
|
303
|
+
@select="teamsOnSelect"
|
|
304
|
+
/>
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<div
|
|
308
|
+
v-if="enableFilter?.status"
|
|
309
|
+
w-64
|
|
310
|
+
>
|
|
311
|
+
<MultiSelect
|
|
312
|
+
:options="statusOptions"
|
|
313
|
+
:selected-options="statusSelectedItems"
|
|
314
|
+
placeholder="Status"
|
|
315
|
+
:custom-attr="statusCustomAttr"
|
|
316
|
+
@select="statusOnSelect"
|
|
317
|
+
/>
|
|
318
|
+
</div>
|
|
319
|
+
|
|
320
|
+
<div
|
|
321
|
+
v-if="enableFilter?.language && languageOptions.length > 0"
|
|
322
|
+
w-48
|
|
138
323
|
>
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
|
|
324
|
+
<MultiSelect
|
|
325
|
+
:options="languageOptions"
|
|
326
|
+
:selected-options="languageSelectedItems"
|
|
327
|
+
placeholder="Language"
|
|
328
|
+
@select="languageOnSelect"
|
|
142
329
|
/>
|
|
143
|
-
|
|
144
|
-
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
<div
|
|
333
|
+
v-if="enableFilterButton"
|
|
334
|
+
>
|
|
335
|
+
<button
|
|
336
|
+
type="button"
|
|
337
|
+
class="flex flex-shrink-0 items-center justify-center border border-gray-200 rounded-lg bg-white px-3 py-2 text-sm font-medium text-gray-900 focus:z-10 dark:border-gray-600 dark:bg-gray-800 hover:bg-gray-100 dark:text-gray-400 hover:text-primary-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
|
|
338
|
+
@click="onFilter"
|
|
339
|
+
>
|
|
340
|
+
<div
|
|
341
|
+
i-material-symbols-search
|
|
342
|
+
mr-1 h-5 w-5
|
|
343
|
+
/>
|
|
344
|
+
Filter
|
|
345
|
+
</button>
|
|
346
|
+
</div>
|
|
145
347
|
</div>
|
|
146
348
|
</div>
|
|
147
349
|
|
|
@@ -53,6 +53,28 @@ const medal = computed((): Medal | null => {
|
|
|
53
53
|
|
|
54
54
|
return null;
|
|
55
55
|
});
|
|
56
|
+
|
|
57
|
+
const place = computed(() => {
|
|
58
|
+
let r = team.value.rank;
|
|
59
|
+
|
|
60
|
+
if (rank.value.contest.organization) {
|
|
61
|
+
r = team.value.organizationRank;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (r === 1) {
|
|
65
|
+
return "1st Place";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (r === 2) {
|
|
69
|
+
return "2nd Place";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (r === 3) {
|
|
73
|
+
return "3rd Place";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return "";
|
|
77
|
+
});
|
|
56
78
|
</script>
|
|
57
79
|
|
|
58
80
|
<template>
|
|
@@ -61,31 +83,39 @@ const medal = computed((): Medal | null => {
|
|
|
61
83
|
text-xl
|
|
62
84
|
font-mono font-semibold italic
|
|
63
85
|
flex flex-col gap-2
|
|
64
|
-
justify-center
|
|
86
|
+
justify-center items-center
|
|
65
87
|
>
|
|
66
|
-
<div>
|
|
67
|
-
Team Rank: {{ team.rank }}
|
|
68
|
-
</div>
|
|
69
|
-
|
|
70
88
|
<div
|
|
71
|
-
|
|
89
|
+
flex flex-col
|
|
90
|
+
items-start
|
|
91
|
+
gap-y-2
|
|
72
92
|
>
|
|
73
|
-
|
|
74
|
-
|
|
93
|
+
<div
|
|
94
|
+
v-if="medal"
|
|
95
|
+
>
|
|
96
|
+
{{ medal.text }} Medalist
|
|
97
|
+
</div>
|
|
75
98
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
<div>
|
|
81
|
-
First Solved Problem {{ firstSolvedProblems }}
|
|
99
|
+
<div
|
|
100
|
+
v-if="place.length > 0"
|
|
101
|
+
>
|
|
102
|
+
{{ place }}
|
|
82
103
|
</div>
|
|
83
|
-
</div>
|
|
84
104
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
105
|
+
<div
|
|
106
|
+
v-if="firstSolvedProblems.length"
|
|
107
|
+
flex flex-col gap-2
|
|
108
|
+
>
|
|
109
|
+
<div>
|
|
110
|
+
First Solved Problem {{ firstSolvedProblems }}
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<div
|
|
115
|
+
v-if="team.solvedProblemNum > 0"
|
|
116
|
+
>
|
|
117
|
+
Solved {{ team.solvedProblemNum }} {{ team.solvedProblemNum > 1 ? 'Problems' : 'Problem' }}
|
|
118
|
+
</div>
|
|
89
119
|
</div>
|
|
90
120
|
</div>
|
|
91
121
|
</template>
|
|
@@ -80,13 +80,26 @@ const types = [TYPE_SUBMISSIONS, TYPE_STATISTICS, TYPE_AWARDS];
|
|
|
80
80
|
width-class="h-8 w-8"
|
|
81
81
|
/>
|
|
82
82
|
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
83
|
+
<Tooltip>
|
|
84
|
+
<h3
|
|
85
|
+
text-gray-900 dark:text-white
|
|
86
|
+
text-2xl
|
|
87
|
+
font-sans font-semibold italic
|
|
88
|
+
>
|
|
89
|
+
{{ headerTitle }}
|
|
90
|
+
</h3>
|
|
91
|
+
|
|
92
|
+
<template #popper>
|
|
93
|
+
<div
|
|
94
|
+
flex flex-col
|
|
95
|
+
justify-start items-start
|
|
96
|
+
>
|
|
97
|
+
<div>
|
|
98
|
+
TeamID: {{ team.id }}
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</template>
|
|
102
|
+
</Tooltip>
|
|
90
103
|
</div>
|
|
91
104
|
|
|
92
105
|
<ModalMenu
|
|
@@ -15,6 +15,10 @@ const resolverUrl = computed(() => {
|
|
|
15
15
|
function goBalloon() {
|
|
16
16
|
router.push(`/balloon/?data-source=${route.path}`);
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
function goCountdown() {
|
|
20
|
+
router.push(`/countdown/?data-source=${route.path}`);
|
|
21
|
+
}
|
|
18
22
|
</script>
|
|
19
23
|
|
|
20
24
|
<template>
|
|
@@ -39,6 +43,19 @@ function goBalloon() {
|
|
|
39
43
|
{{ t('type_menu.balloon') }}
|
|
40
44
|
</button>
|
|
41
45
|
|
|
46
|
+
<button
|
|
47
|
+
btn
|
|
48
|
+
title="Countdown"
|
|
49
|
+
@click="goCountdown"
|
|
50
|
+
>
|
|
51
|
+
{{ t('type_menu.countdown') }}
|
|
52
|
+
</button>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div
|
|
56
|
+
w-full
|
|
57
|
+
flex mt-4 gap-4
|
|
58
|
+
>
|
|
42
59
|
<button
|
|
43
60
|
btn
|
|
44
61
|
title="Submissions"
|
|
@@ -42,7 +42,7 @@ onMounted(() => {
|
|
|
42
42
|
ref="tooltipTargetEl"
|
|
43
43
|
role="tooltip"
|
|
44
44
|
class="tooltip inline-block absolute invisible px-3 py-2 transition-opacity duration-300 shadow-sm opacity-0"
|
|
45
|
-
z-
|
|
45
|
+
z-99999
|
|
46
46
|
rounded
|
|
47
47
|
text-base text-white font-medium
|
|
48
48
|
bg-gray-900 dark:bg-gray-700
|
package/src/components.d.ts
CHANGED
|
@@ -15,8 +15,10 @@ declare module 'vue' {
|
|
|
15
15
|
BottomStatistics: typeof import('./components/board/BottomStatistics.vue')['default']
|
|
16
16
|
ContestIndexUI: typeof import('./components/ContestIndexUI.vue')['default']
|
|
17
17
|
ContestStateBadge: typeof import('./components/board/ContestStateBadge.vue')['default']
|
|
18
|
+
Countdown: typeof import('./components/Countdown.vue')['default']
|
|
18
19
|
CustomBalloon: typeof import('./components/CustomBalloon.vue')['default']
|
|
19
20
|
CustomBoard: typeof import('./components/CustomBoard.vue')['default']
|
|
21
|
+
CustomCountdown: typeof import('./components/CustomCountdown.vue')['default']
|
|
20
22
|
DataSourceInput: typeof import('./components/DataSourceInput.vue')['default']
|
|
21
23
|
Export: typeof import('./components/board/Export.vue')['default']
|
|
22
24
|
Footer: typeof import('./components/Footer.vue')['default']
|
package/src/pages/index.vue
CHANGED
|
@@ -79,15 +79,22 @@ watch(searchText, () => {
|
|
|
79
79
|
>
|
|
80
80
|
<div>
|
|
81
81
|
<div
|
|
82
|
-
v-if="isFetching"
|
|
82
|
+
v-if="isFetching || error"
|
|
83
|
+
mt-4 mb-4
|
|
83
84
|
class="sm:w-[1000px] lg:w-screen"
|
|
84
|
-
flex justify-center
|
|
85
|
+
flex justify-center items-center
|
|
85
86
|
>
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
<div
|
|
88
|
+
v-if="isFetching"
|
|
89
|
+
>
|
|
90
|
+
{{ t("common.loading") }}...
|
|
91
|
+
</div>
|
|
88
92
|
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
<div
|
|
94
|
+
v-if="error"
|
|
95
|
+
>
|
|
96
|
+
{{ error }}
|
|
97
|
+
</div>
|
|
91
98
|
</div>
|
|
92
99
|
|
|
93
100
|
<div
|