@necrolab/dashboard 0.5.15 → 0.5.16
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/backend/api.js +2 -3
- package/eslint.config.js +46 -0
- package/index.html +2 -1
- package/package.json +5 -2
- package/src/App.vue +140 -170
- package/src/assets/css/base/mixins.scss +72 -0
- package/src/assets/css/base/reset.scss +0 -2
- package/src/assets/css/base/scroll.scss +43 -36
- package/src/assets/css/base/typography.scss +9 -10
- package/src/assets/css/base/variables.scss +43 -0
- package/src/assets/css/components/accessibility.scss +37 -0
- package/src/assets/css/components/buttons.scss +58 -15
- package/src/assets/css/components/forms.scss +31 -32
- package/src/assets/css/components/headers.scss +12 -20
- package/src/assets/css/components/modals.scss +2 -2
- package/src/assets/css/components/search-groups.scss +28 -22
- package/src/assets/css/components/tables.scss +5 -7
- package/src/assets/css/components/toasts.scss +7 -7
- package/src/assets/css/components/utilities.scss +220 -0
- package/src/assets/css/main.scss +66 -77
- package/src/components/Auth/LoginForm.vue +5 -84
- package/src/components/Editors/Account/Account.vue +8 -10
- package/src/components/Editors/Account/AccountCreator.vue +28 -59
- package/src/components/Editors/Account/AccountView.vue +38 -86
- package/src/components/Editors/Account/CreateAccount.vue +8 -50
- package/src/components/Editors/Profile/CreateProfile.vue +74 -131
- package/src/components/Editors/Profile/Profile.vue +15 -17
- package/src/components/Editors/Profile/ProfileCountryChooser.vue +16 -60
- package/src/components/Editors/Profile/ProfileView.vue +46 -96
- package/src/components/Editors/TagLabel.vue +16 -55
- package/src/components/Editors/TagToggle.vue +20 -8
- package/src/components/Filter/Filter.vue +62 -75
- package/src/components/Filter/FilterPreview.vue +161 -135
- package/src/components/Filter/PriceSortToggle.vue +36 -43
- package/src/components/Table/Header.vue +1 -1
- package/src/components/Table/Table.vue +45 -51
- package/src/components/Tasks/CheckStock.vue +7 -16
- package/src/components/Tasks/Controls/DesktopControls.vue +15 -60
- package/src/components/Tasks/Controls/MobileControls.vue +5 -20
- package/src/components/Tasks/CreateTaskAXS.vue +20 -118
- package/src/components/Tasks/CreateTaskTM.vue +33 -189
- package/src/components/Tasks/EventDetailRow.vue +21 -0
- package/src/components/Tasks/MassEdit.vue +6 -16
- package/src/components/Tasks/QuickSettings.vue +140 -216
- package/src/components/Tasks/ScrapeVenue.vue +4 -13
- package/src/components/Tasks/Stats.vue +19 -38
- package/src/components/Tasks/Task.vue +65 -268
- package/src/components/Tasks/TaskLabel.vue +9 -3
- package/src/components/Tasks/TaskView.vue +43 -63
- package/src/components/Tasks/Utilities.vue +10 -44
- package/src/components/Tasks/ViewTask.vue +23 -107
- package/src/components/icons/Close.vue +2 -8
- package/src/components/icons/Gear.vue +8 -8
- package/src/components/icons/Hash.vue +5 -0
- package/src/components/icons/Key.vue +2 -8
- package/src/components/icons/Pencil.vue +2 -8
- package/src/components/icons/Profile.vue +2 -8
- package/src/components/icons/Sell.vue +2 -8
- package/src/components/icons/Spinner.vue +4 -7
- package/src/components/icons/SquareCheck.vue +2 -8
- package/src/components/icons/SquareUncheck.vue +2 -8
- package/src/components/icons/Wildcard.vue +2 -8
- package/src/components/icons/index.js +3 -1
- package/src/components/ui/ActionButtonGroup.vue +113 -52
- package/src/components/ui/BalanceIndicator.vue +60 -0
- package/src/components/ui/EmptyState.vue +24 -0
- package/src/components/ui/EnableDisableToggle.vue +23 -0
- package/src/components/ui/FormField.vue +48 -48
- package/src/components/ui/IconLabel.vue +23 -0
- package/src/components/ui/InfoRow.vue +21 -54
- package/src/components/ui/Modal.vue +89 -56
- package/src/components/ui/Navbar.vue +60 -41
- package/src/components/ui/ReadonlyFieldsSection.vue +31 -0
- package/src/components/ui/ReconnectIndicator.vue +111 -124
- package/src/components/ui/SectionCard.vue +6 -14
- package/src/components/ui/Splash.vue +2 -10
- package/src/components/ui/StatusBadge.vue +26 -28
- package/src/components/ui/TaskToggle.vue +54 -0
- package/src/components/ui/controls/CountryChooser.vue +27 -64
- package/src/components/ui/controls/EyeToggle.vue +1 -1
- package/src/components/ui/controls/atomic/Checkbox.vue +40 -121
- package/src/components/ui/controls/atomic/Dropdown.vue +103 -139
- package/src/components/ui/controls/atomic/MultiDropdown.vue +71 -119
- package/src/components/ui/controls/atomic/Switch.vue +21 -84
- package/src/composables/useColorMapping.js +15 -0
- package/src/composables/useCopyToClipboard.js +1 -1
- package/src/composables/useDateFormatting.js +21 -0
- package/src/composables/useDeviceDetection.js +14 -0
- package/src/composables/useDropdownPosition.js +3 -4
- package/src/composables/useDynamicTableHeight.js +31 -0
- package/src/composables/useRowSelection.js +0 -3
- package/src/composables/useTicketPricing.js +16 -0
- package/src/composables/useWindowDimensions.js +21 -0
- package/src/libs/Filter.js +14 -20
- package/src/libs/panzoom.js +1 -5
- package/src/libs/utils/array.js +60 -0
- package/src/{stores/utils.js → libs/utils/dataGeneration.js} +2 -250
- package/src/libs/utils/eventUrl.js +40 -0
- package/src/libs/utils/string.js +28 -0
- package/src/libs/utils/time.js +20 -0
- package/src/libs/utils/validation.js +88 -0
- package/src/main.js +0 -2
- package/src/stores/connection.js +1 -4
- package/src/stores/logger.js +6 -12
- package/src/stores/sampleData.js +1 -2
- package/src/stores/ui.js +59 -36
- package/src/views/Accounts.vue +13 -24
- package/src/views/Console.vue +70 -172
- package/src/views/Editor.vue +211 -379
- package/src/views/FilterBuilder.vue +188 -371
- package/src/views/Login.vue +3 -28
- package/src/views/Profiles.vue +8 -15
- package/src/views/Tasks.vue +49 -36
- package/tailwind.config.js +82 -71
- package/workbox-config.cjs +47 -5
- package/docs/plans/2026-02-08-tailwind-consolidation.md +0 -2438
- package/exit +0 -209
- package/run +0 -177
- package/src/assets/css/base/color-fallbacks.scss +0 -10
- package/switch-branch.sh +0 -41
- /package/public/{reconnect-logo.png → img/reconnect-logo.png} +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="content" class="flex items-start gap-1 text-3xs leading-tight-sm min-h-2.75">
|
|
3
|
+
<svg class="icon-sm" width="10" height="10" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
4
|
+
<slot name="icon"></slot>
|
|
5
|
+
</svg>
|
|
6
|
+
<span :class="['text-light-500 text-3xs leading-tight-sm', truncate ? 'truncate' : '']">{{ content }}</span>
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup>
|
|
11
|
+
defineProps({
|
|
12
|
+
content: {
|
|
13
|
+
type: String,
|
|
14
|
+
required: true
|
|
15
|
+
},
|
|
16
|
+
truncate: {
|
|
17
|
+
type: Boolean,
|
|
18
|
+
default: false
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
</script>
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
<Modal>
|
|
3
3
|
<template #header> Mass Edit Presale Code <i class="fa fa-edit ml-3 mt-1"></i> </template>
|
|
4
4
|
<!-- Event ID -->
|
|
5
|
-
<div class="
|
|
6
|
-
<label class="
|
|
5
|
+
<div class="mt-7 mb-4">
|
|
6
|
+
<label class="flex mb-2 label-override">Event ID <StadiumIcon /></label>
|
|
7
7
|
<div class="input-default required">
|
|
8
8
|
<input
|
|
9
9
|
:placeholder="!isEU(ui.currentCountry.siteId) ? '102PDA9125510GYU' : '529171'"
|
|
@@ -12,33 +12,23 @@
|
|
|
12
12
|
</div>
|
|
13
13
|
</div>
|
|
14
14
|
<!-- Presale Code -->
|
|
15
|
-
<div class="
|
|
16
|
-
<label class="
|
|
15
|
+
<div class="mb-8">
|
|
16
|
+
<label class="flex mb-2 label-override">Presale Code <BagIcon /></label>
|
|
17
17
|
<div class="input-default required">
|
|
18
18
|
<input placeholder="presale code" v-model="code" />
|
|
19
19
|
</div>
|
|
20
20
|
</div>
|
|
21
|
-
<button
|
|
22
|
-
class="button-default bg-dark-400 w-48 text-xs flex items-center justify-center gap-x-2 ml-auto border border-light-300 hover:border-light-400"
|
|
23
|
-
@click="done()"
|
|
24
|
-
>
|
|
21
|
+
<button class="btn-modal ml-auto" @click="done()">
|
|
25
22
|
Edit <EditIcon />
|
|
26
23
|
</button>
|
|
27
24
|
</Modal>
|
|
28
25
|
</template>
|
|
29
|
-
<style lang="scss" scoped>
|
|
30
|
-
.input-wrapper {
|
|
31
|
-
label {
|
|
32
|
-
@apply flex;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
</style>
|
|
36
26
|
<script setup>
|
|
37
27
|
import Modal from "@/components/ui/Modal.vue";
|
|
38
28
|
import { StadiumIcon, BagIcon, EditIcon } from "@/components/icons";
|
|
39
29
|
import { useUIStore } from "@/stores/ui";
|
|
40
30
|
import { ref } from "vue";
|
|
41
|
-
import { isEU } from "@/
|
|
31
|
+
import { isEU } from "@/libs/utils/eventUrl";
|
|
42
32
|
|
|
43
33
|
const ui = useUIStore();
|
|
44
34
|
const code = ref("");
|
|
@@ -9,23 +9,40 @@
|
|
|
9
9
|
<div
|
|
10
10
|
v-for="(value, keyName) in category"
|
|
11
11
|
:key="keyName"
|
|
12
|
-
class="
|
|
12
|
+
class="form-field-labeled-sm mb-3"
|
|
13
13
|
>
|
|
14
14
|
<span class="w-1/3 text-light-300 font-medium text-xs">{{ keyName }}</span>
|
|
15
|
-
<div class="w-2/3 h-full
|
|
15
|
+
<div class="w-2/3 h-full relative">
|
|
16
16
|
<input
|
|
17
17
|
type="text"
|
|
18
|
-
class="
|
|
18
|
+
class="api-key-input pr-16"
|
|
19
19
|
:placeholder="`Enter ${keyName} key`"
|
|
20
20
|
v-model="quickConfig.keys[categoryName][keyName]"
|
|
21
21
|
/>
|
|
22
|
+
<div class="balance-indicator">
|
|
23
|
+
<div v-if="balanceState[categoryName]?.[keyName]?.loading" class="flex items-center gap-1">
|
|
24
|
+
<svg class="animate-spin h-3 w-3 text-light-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
25
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
26
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
27
|
+
</svg>
|
|
28
|
+
</div>
|
|
29
|
+
<div v-else-if="balanceState[categoryName]?.[keyName]?.error" class="flex items-center gap-1" title="Failed to fetch balance">
|
|
30
|
+
<svg class="h-3 w-3 text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
31
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
32
|
+
</svg>
|
|
33
|
+
</div>
|
|
34
|
+
<div v-else-if="balanceState[categoryName]?.[keyName]?.balance !== null && balanceState[categoryName]?.[keyName]?.balance !== undefined" class="flex items-center gap-1 text-green-400" :title="`Balance: ${balanceState[categoryName]?.[keyName]?.balance}`">
|
|
35
|
+
<img src="@/assets/img/sell.svg" width="9" class="opacity-60" />
|
|
36
|
+
<span class="text-2xs font-bold tabular-nums">{{ formatBalance(balanceState[categoryName]?.[keyName]?.balance) }}</span>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
22
39
|
</div>
|
|
23
40
|
</div>
|
|
24
41
|
</div>
|
|
25
42
|
|
|
26
43
|
<div class="text-light-400 mb-2 mt-4">Proxy Lists</div>
|
|
27
44
|
|
|
28
|
-
<div class="
|
|
45
|
+
<div class="form-field-labeled-sm mb-3 relative z-dropdown">
|
|
29
46
|
<span class="w-1/3 text-light-300 font-medium text-xs">Checkout Proxies</span>
|
|
30
47
|
<Dropdown
|
|
31
48
|
class="!w-2/3"
|
|
@@ -38,7 +55,7 @@
|
|
|
38
55
|
/>
|
|
39
56
|
</div>
|
|
40
57
|
|
|
41
|
-
<div class="
|
|
58
|
+
<div class="form-field-labeled-sm mb-3 relative z-dropdown">
|
|
42
59
|
<span class="w-1/3 text-light-300 font-medium text-xs">Queue Proxies</span>
|
|
43
60
|
<Dropdown
|
|
44
61
|
class="!w-2/3"
|
|
@@ -50,163 +67,56 @@
|
|
|
50
67
|
:allowDefault="false"
|
|
51
68
|
/>
|
|
52
69
|
</div>
|
|
53
|
-
<div class="flex mt-5 justify-between flex-row items-center
|
|
54
|
-
<div class="flex gap-
|
|
55
|
-
<div class="
|
|
56
|
-
<span class="
|
|
57
|
-
<span class="
|
|
70
|
+
<div class="flex mt-5 justify-between flex-row items-center gap-3 flex-col sm:flex-row max-xs:gap-3 max-xs:items-stretch">
|
|
71
|
+
<div class="flex gap-2 items-center max-xs:justify-center max-xs:flex-wrap max-xs:gap-2 max-xs:w-full">
|
|
72
|
+
<div class="flex items-center gap-x-1.5 px-2.5 py-1.5 rounded-lg border bg-dark-400 border-dark-550 max-xs:flex-1 max-xs:min-w-0 text-center text-2xs max-sm:gap-x-1">
|
|
73
|
+
<span class="font-medium text-light-500">Dashboard</span>
|
|
74
|
+
<span class="font-bold text-accent-green">v{{ version }}</span>
|
|
58
75
|
</div>
|
|
59
|
-
<div class="
|
|
60
|
-
<span class="
|
|
61
|
-
<span class="
|
|
76
|
+
<div class="flex items-center gap-x-1.5 px-2.5 py-1.5 rounded-lg border bg-dark-400 border-dark-550 max-xs:flex-1 max-xs:min-w-0 text-center text-2xs max-sm:gap-x-1">
|
|
77
|
+
<span class="font-medium text-light-500">Bot</span>
|
|
78
|
+
<span class="font-bold text-accent-green">v{{ ui.botVersion }}</span>
|
|
62
79
|
</div>
|
|
63
80
|
</div>
|
|
64
81
|
|
|
65
|
-
<div class="flex gap-
|
|
82
|
+
<div class="flex gap-2 max-xs:w-full max-xs:flex-col max-xs:gap-2">
|
|
66
83
|
<button
|
|
67
|
-
class="
|
|
84
|
+
class="btn-modal h-9 text-2xs px-2 max-xs:w-full"
|
|
68
85
|
@click="checkBalances()"
|
|
69
86
|
>
|
|
70
|
-
Balances <img src="@/assets/img/sell.svg" width="
|
|
87
|
+
Balances <img src="@/assets/img/sell.svg" width="10" class="ml-1" />
|
|
71
88
|
</button>
|
|
72
89
|
<button
|
|
73
|
-
class="
|
|
90
|
+
class="btn-modal h-9 text-2xs px-2 max-xs:w-full"
|
|
74
91
|
@click="done(true)"
|
|
75
92
|
>
|
|
76
|
-
Save <img src="/img/save.svg" width="
|
|
93
|
+
Save <img src="/img/save.svg" width="10" class="ml-1 invert" />
|
|
77
94
|
</button>
|
|
78
95
|
</div>
|
|
79
96
|
</div>
|
|
80
97
|
</Modal>
|
|
81
98
|
</template>
|
|
82
99
|
<style lang="scss">
|
|
83
|
-
|
|
84
|
-
label {
|
|
85
|
-
@apply flex;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.transparent {
|
|
90
|
-
background: transparent !important;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
.input-container {
|
|
94
|
-
@apply text-white bg-dark-500 mb-3 px-3 rounded-lg border-dark-550 border-2 flex items-center justify-between h-10;
|
|
95
|
-
overflow: visible;
|
|
96
|
-
transition: border-color 0.15s ease;
|
|
97
|
-
|
|
98
|
-
&:focus-within,
|
|
99
|
-
&:has(.dropdown.opened) {
|
|
100
|
-
border-color: oklch(0.72 0.15 145) !important;
|
|
101
|
-
outline: 1px solid oklch(0.72 0.15 145) !important;
|
|
102
|
-
outline-offset: 0;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.quick-settings-input {
|
|
107
|
-
@apply w-full h-full text-sm text-white bg-transparent border-0 outline-none px-2 py-1 transition-colors duration-150;
|
|
108
|
-
|
|
109
|
-
&:focus {
|
|
110
|
-
@apply outline-none border-0 shadow-none bg-transparent;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
&:hover:not(:focus) {
|
|
114
|
-
background: transparent;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
&::placeholder {
|
|
118
|
-
color: oklch(0.50 0 0);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
.input-container:hover {
|
|
123
|
-
border-color: oklch(0.30 0 0);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
.input-container:focus-within {
|
|
127
|
-
border-color: oklch(0.72 0.15 145) !important;
|
|
128
|
-
outline: 1px solid oklch(0.72 0.15 145);
|
|
129
|
-
outline-offset: 0;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/* iPhone portrait mode fixes */
|
|
133
|
-
@media (max-width: 430px) and (orientation: portrait) {
|
|
134
|
-
.quicksettings-bottom {
|
|
135
|
-
flex-direction: column;
|
|
136
|
-
gap: 1rem;
|
|
137
|
-
align-items: stretch;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
.quicksettings-versions {
|
|
141
|
-
justify-content: center;
|
|
142
|
-
flex-wrap: wrap;
|
|
143
|
-
gap: 0.5rem;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
.quicksettings-versions > div {
|
|
147
|
-
flex: 1;
|
|
148
|
-
min-width: 120px;
|
|
149
|
-
text-align: center;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
.quicksettings-buttons {
|
|
153
|
-
justify-content: center;
|
|
154
|
-
gap: 0.75rem;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
.quicksettings-buttons button {
|
|
158
|
-
width: 5rem;
|
|
159
|
-
padding-left: 0.5rem;
|
|
160
|
-
padding-right: 0.5rem;
|
|
161
|
-
gap: 0.25rem;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
.quicksettings-buttons button img {
|
|
165
|
-
width: 12px;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/* Fix modal scrolling on mobile */
|
|
100
|
+
/* Modal mobile scrolling uses :deep() due to teleported element */
|
|
170
101
|
@media (max-width: 768px) {
|
|
171
|
-
:deep(.modal-mask) {
|
|
172
|
-
|
|
173
|
-
overflow-x: hidden !important;
|
|
174
|
-
touch-action: pan-y !important;
|
|
175
|
-
-webkit-overflow-scrolling: touch !important;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
:deep(.component-modal) {
|
|
179
|
-
overflow-y: visible !important;
|
|
180
|
-
overflow-x: hidden !important;
|
|
181
|
-
touch-action: pan-y !important;
|
|
182
|
-
max-height: none !important;
|
|
183
|
-
}
|
|
102
|
+
:deep(.modal-mask) { overflow-y: auto !important; overflow-x: hidden !important; touch-action: pan-y !important; -webkit-overflow-scrolling: touch !important; }
|
|
103
|
+
:deep(.component-modal) { overflow-y: visible !important; overflow-x: hidden !important; touch-action: pan-y !important; max-height: none !important; }
|
|
184
104
|
}
|
|
185
105
|
|
|
186
|
-
/*
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
:deep(.dropdown)
|
|
106
|
+
/* Dropdown styling uses :deep() due to teleport to body */
|
|
107
|
+
:deep(.dropdown),
|
|
108
|
+
:deep(.dropdown.opened),
|
|
109
|
+
:deep(.dropdown:focus-within),
|
|
110
|
+
:deep(.dropdown:hover) {
|
|
190
111
|
background: transparent !important;
|
|
191
112
|
border: none !important;
|
|
192
113
|
box-shadow: none !important;
|
|
193
|
-
padding: 0 !important;
|
|
194
|
-
height: 40px !important;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/* Keep dropdown border hidden when opened (container handles border) */
|
|
198
|
-
:deep(.dropdown.opened),
|
|
199
|
-
:deep(.dropdown:focus-within) {
|
|
200
|
-
border: none !important;
|
|
201
114
|
outline: none !important;
|
|
202
|
-
box-shadow: none !important;
|
|
203
115
|
}
|
|
204
116
|
|
|
205
|
-
:deep(.dropdown
|
|
206
|
-
:
|
|
207
|
-
|
|
208
|
-
background: transparent !important;
|
|
209
|
-
box-shadow: none !important;
|
|
117
|
+
:deep(.dropdown) {
|
|
118
|
+
padding: 0 !important;
|
|
119
|
+
height: 40px !important;
|
|
210
120
|
}
|
|
211
121
|
|
|
212
122
|
:deep(.dropdown-display) {
|
|
@@ -219,6 +129,12 @@
|
|
|
219
129
|
line-height: 1.25rem;
|
|
220
130
|
}
|
|
221
131
|
|
|
132
|
+
:deep(.dropdown-item) {
|
|
133
|
+
padding: 0.625rem 1rem;
|
|
134
|
+
font-size: 0.875rem;
|
|
135
|
+
line-height: 1.25rem;
|
|
136
|
+
}
|
|
137
|
+
|
|
222
138
|
:deep(.dropdown-menu-portal) {
|
|
223
139
|
z-index: 26000 !important;
|
|
224
140
|
max-height: 200px !important;
|
|
@@ -226,8 +142,8 @@
|
|
|
226
142
|
overscroll-behavior: contain !important;
|
|
227
143
|
touch-action: pan-y !important;
|
|
228
144
|
-webkit-overflow-scrolling: touch !important;
|
|
229
|
-
border
|
|
230
|
-
|
|
145
|
+
@apply border border-dark-550 !important;
|
|
146
|
+
@apply bg-gradient-to-br from-dark-800 to-dark-750 !important;
|
|
231
147
|
width: calc(100vw - 4rem) !important;
|
|
232
148
|
max-width: 600px !important;
|
|
233
149
|
|
|
@@ -236,82 +152,21 @@
|
|
|
236
152
|
}
|
|
237
153
|
}
|
|
238
154
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
/* Ensure proper z-index stacking */
|
|
246
|
-
.z-tooltip {
|
|
247
|
-
z-index: 100 !important;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
.z-dropdown {
|
|
251
|
-
z-index: 90 !important;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/* QuickSettings compact buttons */
|
|
255
|
-
.quicksettings-btn {
|
|
256
|
-
@apply rounded transition-all duration-150;
|
|
257
|
-
background: oklch(0.2046 0 0);
|
|
258
|
-
border: 2px solid oklch(0.2809 0 0);
|
|
259
|
-
color: oklch(0.90 0 0);
|
|
260
|
-
height: 2.5rem;
|
|
261
|
-
width: 6.5rem;
|
|
262
|
-
font-size: 0.75rem;
|
|
263
|
-
font-weight: 500;
|
|
155
|
+
.api-key-input {
|
|
156
|
+
@apply w-full h-full text-sm text-white border-0 rounded-md;
|
|
157
|
+
@apply px-3 py-2;
|
|
158
|
+
@apply focus:outline-none;
|
|
159
|
+
background: oklch(26% 0 68deg);
|
|
264
160
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
&:active, &:focus {
|
|
270
|
-
border-color: oklch(0.72 0.15 145);
|
|
271
|
-
outline: 1px solid oklch(0.72 0.15 145);
|
|
272
|
-
outline-offset: 0;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
@media (max-width: 640px) {
|
|
276
|
-
height: 2.25rem;
|
|
277
|
-
width: 5rem;
|
|
278
|
-
font-size: 0.7rem;
|
|
279
|
-
padding: 0 0.5rem;
|
|
280
|
-
|
|
281
|
-
img {
|
|
282
|
-
width: 10px !important;
|
|
283
|
-
}
|
|
161
|
+
&::placeholder {
|
|
162
|
+
@apply text-light-500;
|
|
284
163
|
}
|
|
285
164
|
}
|
|
286
165
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
@apply flex items-center
|
|
290
|
-
|
|
291
|
-
border-color: oklch(0.2809 0 0);
|
|
292
|
-
|
|
293
|
-
.version-label {
|
|
294
|
-
@apply text-xs font-medium;
|
|
295
|
-
color: oklch(0.65 0 0);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
.version-number {
|
|
299
|
-
@apply text-xs font-bold;
|
|
300
|
-
color: oklch(0.72 0.15 145);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
@media (max-width: 640px) {
|
|
304
|
-
padding: 0.375rem 0.625rem;
|
|
305
|
-
gap: 0.375rem;
|
|
306
|
-
|
|
307
|
-
.version-label {
|
|
308
|
-
font-size: 0.625rem;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
.version-number {
|
|
312
|
-
font-size: 0.625rem;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
166
|
+
.balance-indicator {
|
|
167
|
+
@apply absolute right-3 top-1/2 -translate-y-1/2;
|
|
168
|
+
@apply flex items-center justify-center;
|
|
169
|
+
@apply pointer-events-none;
|
|
315
170
|
}
|
|
316
171
|
</style>
|
|
317
172
|
<script setup>
|
|
@@ -319,15 +174,14 @@ import Modal from "@/components/ui/Modal.vue";
|
|
|
319
174
|
import { GearIcon } from "@/components/icons";
|
|
320
175
|
import { useUIStore } from "@/stores/ui";
|
|
321
176
|
import { ref } from "vue";
|
|
322
|
-
import Switch from "@/components/ui/controls/atomic/Switch.vue";
|
|
323
177
|
import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
|
|
324
|
-
import { sendQuickConfig, getQuickConfig } from "@/stores/requests";
|
|
325
|
-
import { getBalances, sendProxyList, getProxyLists } from "../../stores/requests";
|
|
178
|
+
import { sendQuickConfig, getQuickConfig, getBalances, sendProxyList, getProxyLists } from "@/stores/requests";
|
|
326
179
|
|
|
327
180
|
const version = __APP_VERSION__;
|
|
328
181
|
const quickConfig = ref({});
|
|
329
182
|
const ui = useUIStore();
|
|
330
183
|
const proxyLists = ref(["loading..."]);
|
|
184
|
+
const balanceState = ref({});
|
|
331
185
|
|
|
332
186
|
const loadCurrentQuickConfig = async () => {
|
|
333
187
|
try {
|
|
@@ -367,12 +221,82 @@ const loadProxyLists = async () => {
|
|
|
367
221
|
};
|
|
368
222
|
|
|
369
223
|
const checkBalances = async () => {
|
|
224
|
+
// Initialize loading state for all keys
|
|
225
|
+
Object.entries(quickConfig.value.keys || {}).forEach(([categoryName, category]) => {
|
|
226
|
+
if (!balanceState.value[categoryName]) {
|
|
227
|
+
balanceState.value[categoryName] = {};
|
|
228
|
+
}
|
|
229
|
+
Object.keys(category).forEach((keyName) => {
|
|
230
|
+
balanceState.value[categoryName][keyName] = {
|
|
231
|
+
loading: true,
|
|
232
|
+
error: false,
|
|
233
|
+
balance: null
|
|
234
|
+
};
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
370
238
|
const balances = await getBalances();
|
|
371
|
-
|
|
239
|
+
|
|
240
|
+
if (typeof balances === "object") {
|
|
372
241
|
Object.entries(balances).forEach(([service, balance]) => {
|
|
373
|
-
|
|
242
|
+
// Find which category and key this service belongs to
|
|
243
|
+
Object.entries(quickConfig.value.keys || {}).forEach(([categoryName, category]) => {
|
|
244
|
+
Object.keys(category).forEach((keyName) => {
|
|
245
|
+
// Match service name (case-insensitive)
|
|
246
|
+
if (keyName.toLowerCase().includes(service.toLowerCase()) ||
|
|
247
|
+
service.toLowerCase().includes(keyName.toLowerCase())) {
|
|
248
|
+
balanceState.value[categoryName][keyName] = {
|
|
249
|
+
loading: false,
|
|
250
|
+
error: false,
|
|
251
|
+
balance: balance
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Mark any keys that didn't get a response as errors
|
|
259
|
+
Object.entries(balanceState.value).forEach(([categoryName, category]) => {
|
|
260
|
+
Object.entries(category).forEach(([keyName, state]) => {
|
|
261
|
+
if (state.loading) {
|
|
262
|
+
balanceState.value[categoryName][keyName] = {
|
|
263
|
+
loading: false,
|
|
264
|
+
error: true,
|
|
265
|
+
balance: null
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
ui.showSuccess("Balances updated");
|
|
272
|
+
} else {
|
|
273
|
+
// Mark all as error
|
|
274
|
+
Object.entries(balanceState.value).forEach(([categoryName, category]) => {
|
|
275
|
+
Object.keys(category).forEach((keyName) => {
|
|
276
|
+
balanceState.value[categoryName][keyName] = {
|
|
277
|
+
loading: false,
|
|
278
|
+
error: true,
|
|
279
|
+
balance: null
|
|
280
|
+
};
|
|
281
|
+
});
|
|
374
282
|
});
|
|
375
|
-
|
|
283
|
+
ui.showError("Could not get balances");
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const formatBalance = (balance) => {
|
|
288
|
+
if (typeof balance === 'number') {
|
|
289
|
+
// Format large numbers with K, M suffixes if needed
|
|
290
|
+
if (balance >= 1000000) {
|
|
291
|
+
return (balance / 1000000).toFixed(1) + 'M';
|
|
292
|
+
} else if (balance >= 100000) {
|
|
293
|
+
return (balance / 1000).toFixed(0) + 'K';
|
|
294
|
+
} else if (balance >= 10000) {
|
|
295
|
+
return (balance / 1000).toFixed(1) + 'K';
|
|
296
|
+
}
|
|
297
|
+
return balance.toLocaleString();
|
|
298
|
+
}
|
|
299
|
+
return String(balance);
|
|
376
300
|
};
|
|
377
301
|
|
|
378
302
|
loadProxyLists();
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<template #header> Scrape Venue <ScrapeIcon class="ml-4" /></template>
|
|
4
4
|
<!-- Event ID -->
|
|
5
5
|
<div class="input-wrapper mt-7 mb-4">
|
|
6
|
-
<label class="label-override mb-2">Event ID or URL <StadiumIcon /></label>
|
|
6
|
+
<label class="label-override mb-2 flex">Event ID or URL <StadiumIcon /></label>
|
|
7
7
|
<div class="input-default required">
|
|
8
8
|
<input
|
|
9
9
|
:placeholder="!isEU(ui.currentCountry.siteId) ? '102PDA9125510GYU' : '529171'"
|
|
@@ -11,27 +11,18 @@
|
|
|
11
11
|
/>
|
|
12
12
|
</div>
|
|
13
13
|
</div>
|
|
14
|
-
<button
|
|
15
|
-
class="button-default ml-auto mt-4 flex w-48 items-center justify-center gap-x-2 bg-dark-400 text-xs"
|
|
16
|
-
@click="done()">
|
|
14
|
+
<button class="btn-modal ml-auto mt-4 w-48" @click="done()">
|
|
17
15
|
Scrape Venue
|
|
18
|
-
<ScrapeIcon />
|
|
16
|
+
<ScrapeIcon class="ml-2" />
|
|
19
17
|
</button>
|
|
20
18
|
</Modal>
|
|
21
19
|
</template>
|
|
22
|
-
<style lang="scss" scoped>
|
|
23
|
-
.input-wrapper {
|
|
24
|
-
label {
|
|
25
|
-
@apply flex;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
</style>
|
|
29
20
|
<script setup>
|
|
30
21
|
import Modal from "@/components/ui/Modal.vue";
|
|
31
22
|
import { StadiumIcon, ScrapeIcon } from "@/components/icons";
|
|
32
23
|
import { useUIStore } from "@/stores/ui";
|
|
33
24
|
import { ref } from "vue";
|
|
34
|
-
import { isEU } from "@/
|
|
25
|
+
import { isEU } from "@/libs/utils/eventUrl";
|
|
35
26
|
|
|
36
27
|
const ui = useUIStore();
|
|
37
28
|
const eventId = ref("");
|