@necrolab/dashboard 0.4.61 → 0.4.208
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/.prettierrc +1 -27
- package/.vscode/extensions.json +1 -1
- package/README.md +79 -43
- package/backend/api.js +48 -40
- package/backend/auth.js +3 -3
- package/backend/batching.js +1 -1
- package/backend/endpoints.js +77 -13
- package/backend/index.js +2 -2
- package/backend/mock-data.js +38 -29
- package/backend/mock-src/classes/logger.js +8 -8
- package/backend/mock-src/classes/utils.js +3 -7
- package/backend/mock-src/database.js +0 -0
- package/backend/mock-src/ticketmaster.js +79 -79
- package/backend/validator.js +2 -2
- package/config/configs.json +3 -2
- package/config/filter.json +3 -2
- package/index.html +10 -81
- package/index.js +1 -1
- package/package.json +25 -40
- package/postcss.config.js +1 -1
- package/postinstall.js +17 -98
- package/public/android-chrome-192x192.png +0 -0
- package/public/android-chrome-512x512.png +0 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/manifest.json +7 -12
- package/public/sw.js +2 -0
- package/public/workbox-49fdaf31.js +2 -0
- package/public/workbox-49fdaf31.js.map +1 -0
- package/public/workbox-88575b92.js +2 -0
- package/public/workbox-88575b92.js.map +1 -0
- package/public/workbox-a67a7b11.js +2 -0
- package/public/workbox-a67a7b11.js.map +1 -0
- package/public/workbox-d4314735.js +2 -0
- package/public/workbox-d4314735.js.map +1 -0
- package/public/workbox-e0f89ef3.js +2 -0
- package/public/workbox-e0f89ef3.js.map +1 -0
- package/run +9 -176
- package/src/App.vue +85 -498
- package/src/assets/css/_input.scss +99 -144
- package/src/assets/css/main.scss +99 -450
- package/src/assets/img/background.svg +2 -2
- package/src/assets/img/logo_icon.png +0 -0
- package/src/components/Auth/LoginForm.vue +11 -62
- package/src/components/Editors/Account/Account.vue +40 -116
- package/src/components/Editors/Account/AccountCreator.vue +39 -88
- package/src/components/Editors/Account/AccountView.vue +34 -102
- package/src/components/Editors/Account/CreateAccount.vue +32 -80
- package/src/components/Editors/Profile/CreateProfile.vue +83 -269
- package/src/components/Editors/Profile/Profile.vue +47 -132
- package/src/components/Editors/Profile/ProfileCountryChooser.vue +20 -82
- package/src/components/Editors/Profile/ProfileView.vue +34 -91
- package/src/components/Editors/TagLabel.vue +6 -67
- package/src/components/Filter/Filter.vue +72 -289
- package/src/components/Filter/FilterPreview.vue +30 -171
- package/src/components/Filter/PriceSortToggle.vue +4 -74
- package/src/components/Table/Header.vue +1 -1
- package/src/components/Table/Row.vue +1 -1
- package/src/components/Table/Table.vue +2 -19
- package/src/components/Tasks/CheckStock.vue +13 -28
- package/src/components/Tasks/Controls/DesktopControls.vue +17 -17
- package/src/components/Tasks/Controls/MobileControls.vue +45 -8
- package/src/components/Tasks/CreateTaskAXS.vue +73 -79
- package/src/components/Tasks/CreateTaskTM.vue +142 -94
- package/src/components/Tasks/MassEdit.vue +7 -9
- package/src/components/Tasks/QuickSettings.vue +55 -169
- package/src/components/Tasks/ScrapeVenue.vue +4 -7
- package/src/components/Tasks/Stats.vue +23 -52
- package/src/components/Tasks/Task.vue +136 -378
- package/src/components/Tasks/TaskView.vue +47 -107
- package/src/components/Tasks/Utilities.vue +6 -5
- package/src/components/icons/Bag.vue +1 -1
- package/src/components/icons/Loyalty.vue +1 -1
- package/src/components/icons/Mail.vue +2 -2
- package/src/components/icons/Play.vue +2 -2
- package/src/components/icons/Reload.vue +5 -4
- package/src/components/icons/Sandclock.vue +2 -2
- package/src/components/icons/Stadium.vue +1 -1
- package/src/components/icons/index.js +1 -24
- package/src/components/ui/Modal.vue +13 -105
- package/src/components/ui/Navbar.vue +38 -171
- package/src/components/ui/ReconnectIndicator.vue +55 -351
- package/src/components/ui/Splash.vue +35 -5
- package/src/components/ui/controls/CountryChooser.vue +62 -200
- package/src/components/ui/controls/atomic/Checkbox.vue +10 -119
- package/src/components/ui/controls/atomic/Dropdown.vue +39 -208
- package/src/components/ui/controls/atomic/MultiDropdown.vue +37 -300
- package/src/libs/Filter.js +170 -200
- package/src/registerServiceWorker.js +1 -1
- package/src/stores/connection.js +53 -51
- package/src/stores/logger.js +3 -11
- package/src/stores/sampleData.js +235 -207
- package/src/stores/ui.js +44 -112
- package/src/stores/utils.js +6 -90
- package/src/views/Accounts.vue +35 -44
- package/src/views/Console.vue +90 -341
- package/src/views/Editor.vue +123 -1176
- package/src/views/FilterBuilder.vue +251 -607
- package/src/views/Login.vue +14 -76
- package/src/views/Profiles.vue +25 -44
- package/src/views/Tasks.vue +100 -187
- package/static/offline.html +50 -192
- package/tailwind.config.js +26 -104
- package/vite.config.js +16 -73
- package/vue.config.js +32 -0
- package/workbox-config.js +11 -0
- package/artwork/image.png +0 -0
- package/dev-server.js +0 -136
- package/exit +0 -209
- package/jsconfig.json +0 -16
- package/src/assets/css/_utilities.scss +0 -388
- package/src/assets/img/background.svg.backup +0 -11
- package/src/components/icons/Check.vue +0 -5
- package/src/components/icons/Close.vue +0 -21
- package/src/components/icons/CloseX.vue +0 -5
- package/src/components/icons/Key.vue +0 -21
- package/src/components/icons/Pencil.vue +0 -21
- package/src/components/icons/Profile.vue +0 -18
- package/src/components/icons/Sell.vue +0 -21
- package/src/components/icons/Spinner.vue +0 -42
- package/src/components/icons/SquareCheck.vue +0 -18
- package/src/components/icons/SquareUncheck.vue +0 -18
- package/src/components/icons/Wildcard.vue +0 -18
- package/src/components/ui/controls/atomic/LoadingButton.vue +0 -45
- package/src/composables/useClickOutside.js +0 -21
- package/src/composables/useDropdownPosition.js +0 -174
- package/src/types/index.js +0 -41
- package/src/utils/debug.js +0 -1
- package/switch-branch.sh +0 -41
- package/workbox-config.cjs +0 -63
- /package/src/assets/img/{logo_icon-old.png → logo_icon_2.png} +0 -0
|
@@ -1,147 +1,104 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
class="
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<div class="flex-1 min-w-0">
|
|
17
|
-
<h3 class="font-medium text-white group-hover:text-light-400 transition-colors text-sm sm:text-base truncate">{{ filterTitle }}</h3>
|
|
18
|
-
<p class="text-xs mt-1 truncate text-light-400" v-if="getFilterSubtitle()">{{ getFilterSubtitle() }}</p>
|
|
19
|
-
</div>
|
|
2
|
+
<div class="text-white text-sm border-t-2 border-t-dark-550">
|
|
3
|
+
<div class="grid grid-cols-7 items-center">
|
|
4
|
+
<div class="col-span-6 m-4 sm:m-1 mr-4">
|
|
5
|
+
<div class="flex items-center h-7 gap-2">
|
|
6
|
+
<h3 @click="handleFilterClick(filter.id)">{{ filterTitle }}</h3>
|
|
7
|
+
<Checkbox
|
|
8
|
+
:class="`${filterBuilder.selectedFilters.includes(filter.id) ? 'border-green-400' : null}`"
|
|
9
|
+
:toggled="filterBuilder.isForCurrentEvent(props.filter)"
|
|
10
|
+
@click="
|
|
11
|
+
filterBuilder.selectedFilters.includes(filter.id)
|
|
12
|
+
? filterBuilder.unselectFilter(filter.id)
|
|
13
|
+
: filterBuilder.selectFilter(filter.id)
|
|
14
|
+
"
|
|
15
|
+
/>
|
|
20
16
|
</div>
|
|
21
|
-
<div
|
|
17
|
+
<div @click="handleFilterClick(filter.id)" v-if="filterBuilder.expandedFilter === filter.id">
|
|
22
18
|
<!-- NORMAL -->
|
|
23
|
-
<div v-if="filterType === 2"
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
</div>
|
|
28
|
-
<div class="info-row" v-if="filter.rows?.length">
|
|
29
|
-
<span class="label">Rows:</span>
|
|
30
|
-
<span class="value">{{ filter?.rows?.join(", ") }}</span>
|
|
31
|
-
</div>
|
|
32
|
-
<div class="info-row">
|
|
33
|
-
<span class="label">Event:</span>
|
|
34
|
-
<span class="value">{{ filter?.event || filter?.eventId || "-" }}</span>
|
|
35
|
-
</div>
|
|
19
|
+
<div v-if="filterType === 2">
|
|
20
|
+
<p>Section: {{ filter?.section }}</p>
|
|
21
|
+
<p v-if="filter.rows?.length">Rows: {{ filter?.rows?.join(", ") }}</p>
|
|
22
|
+
<p>Event: {{ filter?.event || filter?.eventId || "-" }}</p>
|
|
36
23
|
</div>
|
|
37
24
|
<!-- NORMAL_FIRSTROW -->
|
|
38
|
-
<div v-if="filterType === 3"
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
</div>
|
|
43
|
-
<div class="info-row">
|
|
44
|
-
<span class="label">Row:</span>
|
|
45
|
-
<span class="value">{{ filter?._row }}</span>
|
|
46
|
-
</div>
|
|
47
|
-
<div class="info-row">
|
|
48
|
-
<span class="label">Event:</span>
|
|
49
|
-
<span class="value">{{ filter?.event || "-" }}</span>
|
|
50
|
-
</div>
|
|
25
|
+
<div v-if="filterType === 3">
|
|
26
|
+
<p>Section: {{ filter?.section }}</p>
|
|
27
|
+
<p>Row: {{ filter?._row }}</p>
|
|
28
|
+
<p>Event: {{ filter?.event || "-" }}</p>
|
|
51
29
|
</div>
|
|
52
30
|
<!-- CATCH_ALL_GA -->
|
|
53
31
|
<div v-if="filterType === 1">
|
|
54
32
|
<button
|
|
55
|
-
@click
|
|
56
|
-
class="
|
|
33
|
+
@click="update({ buyAny: true, floor: false, generalAdmission: false })"
|
|
34
|
+
class="border-2 rounded border-dark-550 px-1 h-8 my-2 text-gray bg-dark-550 overflow-hidden"
|
|
57
35
|
>
|
|
58
|
-
<component :is="'WildcardIcon'" class="w-4 h-4" />
|
|
59
36
|
Convert to wildcard
|
|
60
37
|
</button>
|
|
61
38
|
</div>
|
|
62
39
|
<!-- CATCH_ALL -->
|
|
63
40
|
<div v-if="filterType === 0">
|
|
64
41
|
<button
|
|
65
|
-
@click
|
|
66
|
-
class="
|
|
42
|
+
@click="update({ floor: true, buyAny: false, generalAdmission: false })"
|
|
43
|
+
class="border-2 rounded border-dark-550 px-1 h-8 text-gray my-2 bg-dark-550 overflow-hidden"
|
|
67
44
|
>
|
|
68
|
-
<component :is="'BoxIcon'" class="w-4 h-4" />
|
|
69
45
|
Convert to Floor Wildcard
|
|
70
46
|
</button>
|
|
71
47
|
</div>
|
|
72
48
|
<!-- CATCH_ALL_FLOOR -->
|
|
73
49
|
<div v-if="filterType === 5">
|
|
74
50
|
<button
|
|
75
|
-
@click
|
|
76
|
-
class="
|
|
51
|
+
@click="update({ generalAdmission: true, floor: false, buyAny: false })"
|
|
52
|
+
class="border-2 rounded border-dark-550 px-1 h-8 text-gray my-2 bg-dark-550 overflow-hidden"
|
|
77
53
|
>
|
|
78
|
-
<component :is="'GroupIcon'" class="w-4 h-4" />
|
|
79
54
|
Convert to GA Wildcard
|
|
80
55
|
</button>
|
|
81
56
|
</div>
|
|
82
57
|
</div>
|
|
83
|
-
<div v-if="filterBuilder.expandedFilter === filter.id" class="
|
|
84
|
-
<div class="flex
|
|
85
|
-
<
|
|
86
|
-
|
|
87
|
-
<label class="text-sm font-medium">Excluded</label>
|
|
88
|
-
</div>
|
|
89
|
-
<div class="flex items-center gap-2 control-group">
|
|
90
|
-
<label class="text-xs whitespace-nowrap text-light-400">Min:</label>
|
|
91
|
-
<input
|
|
92
|
-
type="number"
|
|
93
|
-
:value="filter.minPrice"
|
|
94
|
-
@change="
|
|
95
|
-
(e) =>
|
|
96
|
-
update(
|
|
97
|
-
{
|
|
98
|
-
minPrice: e.target.value - 0
|
|
99
|
-
},
|
|
100
|
-
true
|
|
101
|
-
)
|
|
102
|
-
"
|
|
103
|
-
placeholder="0"
|
|
104
|
-
class="filter-input w-16 sm:w-20"
|
|
105
|
-
/>
|
|
106
|
-
</div>
|
|
107
|
-
<div class="flex items-center gap-2 control-group">
|
|
108
|
-
<label class="text-xs whitespace-nowrap text-light-400">Max:</label>
|
|
109
|
-
<input
|
|
110
|
-
type="number"
|
|
111
|
-
:value="filter.maxPrice"
|
|
112
|
-
@change="
|
|
113
|
-
(e) =>
|
|
114
|
-
update(
|
|
115
|
-
{
|
|
116
|
-
maxPrice: e.target.value - 0
|
|
117
|
-
},
|
|
118
|
-
true
|
|
119
|
-
)
|
|
120
|
-
"
|
|
121
|
-
placeholder="1000"
|
|
122
|
-
class="filter-input w-16 sm:w-20"
|
|
123
|
-
/>
|
|
124
|
-
</div>
|
|
125
|
-
<div class="control-group">
|
|
126
|
-
<PriceSortToggle
|
|
127
|
-
:current="filter.priceSort"
|
|
128
|
-
@change="(e) => props.filterBuilder.replaceById(filter.id, { priceSort: e })"
|
|
129
|
-
/>
|
|
130
|
-
</div>
|
|
58
|
+
<div v-if="filterBuilder.expandedFilter === filter.id" class="flex gap-2 items-center">
|
|
59
|
+
<div class="flex items-center gap-2">
|
|
60
|
+
<Checkbox :toggled="filter.exclude || false" @valueUpdate="handleExcludeClick" />
|
|
61
|
+
<p>Excluded</p>
|
|
131
62
|
</div>
|
|
63
|
+
<input
|
|
64
|
+
type="text"
|
|
65
|
+
:value="filter.minPrice"
|
|
66
|
+
@change="
|
|
67
|
+
(e) =>
|
|
68
|
+
update(
|
|
69
|
+
{
|
|
70
|
+
minPrice: e.target.value - 0
|
|
71
|
+
},
|
|
72
|
+
true
|
|
73
|
+
)
|
|
74
|
+
"
|
|
75
|
+
placeholder="min"
|
|
76
|
+
class="w-16 bg-dark-500 border-2 rounded border-dark-550 px-1 h-8"
|
|
77
|
+
/>
|
|
78
|
+
<input
|
|
79
|
+
type="number"
|
|
80
|
+
:value="filter.maxPrice"
|
|
81
|
+
@change="
|
|
82
|
+
(e) =>
|
|
83
|
+
update(
|
|
84
|
+
{
|
|
85
|
+
maxPrice: e.target.value - 0
|
|
86
|
+
},
|
|
87
|
+
true
|
|
88
|
+
)
|
|
89
|
+
"
|
|
90
|
+
placeholder="max"
|
|
91
|
+
class="w-16 bg-dark-500 border-2 rounded border-dark-550 px-1 h-8"
|
|
92
|
+
/>
|
|
93
|
+
<PriceSortToggle
|
|
94
|
+
:current="filter.priceSort"
|
|
95
|
+
@change="(e) => props.filterBuilder.replaceById(filter.id, { priceSort: e })"
|
|
96
|
+
/>
|
|
132
97
|
</div>
|
|
133
98
|
</div>
|
|
134
|
-
<div class="col-span-
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
</div>
|
|
138
|
-
<button
|
|
139
|
-
@click="filterBuilder.deleteFilterById(filter.id)"
|
|
140
|
-
class="delete-btn filter-action-btn"
|
|
141
|
-
title="Delete filter"
|
|
142
|
-
>
|
|
143
|
-
<TrashIcon class="w-3 h-3 sm:w-4 sm:h-4" />
|
|
144
|
-
</button>
|
|
99
|
+
<div class="col-span-1 flex justify-self-end m-1 items-center gap-2">
|
|
100
|
+
<MenuIcon class="scale-75 cursor-grab handle" />
|
|
101
|
+
<TrashIcon class="cursor-pointer" @click="filterBuilder.deleteFilterById(filter.id)" />
|
|
145
102
|
</div>
|
|
146
103
|
</div>
|
|
147
104
|
</div>
|
|
@@ -156,7 +113,7 @@ import { useUIStore } from "@/stores/ui";
|
|
|
156
113
|
const ui = useUIStore();
|
|
157
114
|
|
|
158
115
|
// eslint-disable-next-line no-unused-vars
|
|
159
|
-
import { UpIcon, DownIcon, ReloadIcon, TrashIcon, MenuIcon
|
|
116
|
+
import { UpIcon, DownIcon, ReloadIcon, TrashIcon, MenuIcon } from "@/components/icons";
|
|
160
117
|
|
|
161
118
|
let isAllRows = ref(false);
|
|
162
119
|
|
|
@@ -179,46 +136,6 @@ const handleFilterClick = (id) => {
|
|
|
179
136
|
};
|
|
180
137
|
|
|
181
138
|
const filterTypes = props.filterBuilder.filterTypes;
|
|
182
|
-
|
|
183
|
-
const getFilterIcon = () => {
|
|
184
|
-
switch (filterType.value) {
|
|
185
|
-
case filterTypes.CATCH_ALL:
|
|
186
|
-
return WildcardIcon;
|
|
187
|
-
case filterTypes.CATCH_ALL_GA:
|
|
188
|
-
return GroupIcon;
|
|
189
|
-
case filterTypes.CATCH_ALL_FLOOR:
|
|
190
|
-
return BoxIcon;
|
|
191
|
-
case filterTypes.NORMAL:
|
|
192
|
-
case filterTypes.NORMAL_FIRSTROW:
|
|
193
|
-
return StadiumIcon;
|
|
194
|
-
default:
|
|
195
|
-
return FilterIcon;
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
const getFilterSubtitle = () => {
|
|
200
|
-
switch (filterType.value) {
|
|
201
|
-
case filterTypes.NORMAL: {
|
|
202
|
-
const selectedRowAmount = filter.value?.rows?.length;
|
|
203
|
-
if (filter.value?.rows?.length && selectedRowAmount <= 5) {
|
|
204
|
-
return `Rows: ${filter.value.rows.join(", ")}`;
|
|
205
|
-
} else if (selectedRowAmount > 5) {
|
|
206
|
-
return `${selectedRowAmount} rows selected`;
|
|
207
|
-
}
|
|
208
|
-
return selectedRowAmount === 0 ? "Full section" : null;
|
|
209
|
-
}
|
|
210
|
-
case filterTypes.NORMAL_FIRSTROW:
|
|
211
|
-
return `First row in ${filter.value.section}`;
|
|
212
|
-
case filterTypes.CATCH_ALL:
|
|
213
|
-
return "Matches all tickets";
|
|
214
|
-
case filterTypes.CATCH_ALL_GA:
|
|
215
|
-
return "Matches all GA tickets";
|
|
216
|
-
case filterTypes.CATCH_ALL_FLOOR:
|
|
217
|
-
return "Matches all floor tickets";
|
|
218
|
-
default:
|
|
219
|
-
return null;
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
139
|
const getFilterTitle = () => {
|
|
223
140
|
const excluded = filter.value.exclude ? "[BL]" : "";
|
|
224
141
|
switch (filterType.value) {
|
|
@@ -235,7 +152,7 @@ const getFilterTitle = () => {
|
|
|
235
152
|
return `${filter.value.section} (first row) ${excluded}`;
|
|
236
153
|
case filterTypes.NORMAL: {
|
|
237
154
|
const selectedRowAmount = filter.value?.rows?.length;
|
|
238
|
-
const totalRowAmount = document.querySelectorAll(`path[
|
|
155
|
+
const totalRowAmount = document.querySelectorAll(`path[sectionname='${filter.value.section}']`)?.length;
|
|
239
156
|
isAllRows.value = selectedRowAmount === totalRowAmount || !filter.value?.rows?.length;
|
|
240
157
|
|
|
241
158
|
if (isAllRows.value) return `${filter.value.section} (full section) ${excluded}`;
|
|
@@ -285,137 +202,3 @@ props.filterBuilder.onUpdate(() => {
|
|
|
285
202
|
// else if (newData.id === filter.value.id) update(newData);
|
|
286
203
|
});
|
|
287
204
|
</script>
|
|
288
|
-
|
|
289
|
-
<style scoped>
|
|
290
|
-
.filter-card {
|
|
291
|
-
@apply bg-dark-500 border-dark-550 relative;
|
|
292
|
-
border: 1px solid rgba(61, 62, 68, 0.3);
|
|
293
|
-
margin-bottom: 8px;
|
|
294
|
-
transition: all 0.15s ease-out;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
.filter-card:hover:not(.expanded-filter) {
|
|
298
|
-
border-color: rgba(61, 62, 68, 0.6);
|
|
299
|
-
background-color: rgba(46, 47, 52, 0.8);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
.expanded-filter:hover {
|
|
303
|
-
border-color: rgba(61, 62, 68, 0.8);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
.filter-card + .filter-card {
|
|
307
|
-
border-top: 1px solid rgba(61, 62, 68, 0.2);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
.filter-type-badge {
|
|
311
|
-
@apply w-6 h-6 sm:w-8 sm:h-8 rounded-full bg-dark-400 flex items-center justify-center flex-shrink-0;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
.info-row {
|
|
315
|
-
@apply flex justify-between items-center;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
.label {
|
|
319
|
-
@apply text-xs font-medium text-light-400;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
.value {
|
|
323
|
-
@apply text-sm text-white;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
.conversion-btn {
|
|
327
|
-
@apply flex items-center gap-2 px-3 py-2 bg-dark-400 hover:bg-dark-300 border border-dark-550 rounded-md text-sm font-medium transition-colors;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
.control-group {
|
|
331
|
-
@apply flex items-center gap-2;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
.filter-input {
|
|
335
|
-
@apply border border-dark-550 rounded px-2 py-1.5 text-sm text-white focus:outline-none transition-colors;
|
|
336
|
-
background-color: rgba(35, 36, 41, 0.9);
|
|
337
|
-
color: white;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
.filter-input:focus {
|
|
341
|
-
border-color: #44454b;
|
|
342
|
-
background-color: rgba(46, 47, 52, 0.9);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
.filter-input::placeholder {
|
|
346
|
-
color: #9CA3AF;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
.filter-action-btn {
|
|
350
|
-
@apply flex items-center justify-center rounded-full transition-all duration-200 border-2 border-transparent;
|
|
351
|
-
width: 28px;
|
|
352
|
-
height: 28px;
|
|
353
|
-
color: #9CA3AF;
|
|
354
|
-
background-color: rgba(35, 36, 41, 0.8);
|
|
355
|
-
backdrop-filter: blur(4px);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
@media (min-width: 640px) {
|
|
359
|
-
.filter-action-btn {
|
|
360
|
-
width: 32px;
|
|
361
|
-
height: 32px;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
.filter-action-btn:hover {
|
|
366
|
-
color: white;
|
|
367
|
-
transform: scale(1.15);
|
|
368
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
.drag-btn:hover {
|
|
372
|
-
background-color: rgba(68, 69, 75, 0.8);
|
|
373
|
-
border-color: #44454b;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
.delete-btn:hover {
|
|
377
|
-
background-color: rgba(239, 68, 68, 0.8);
|
|
378
|
-
border-color: #F87171;
|
|
379
|
-
color: #F87171;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
.drag-handle.sortable-chosen {
|
|
383
|
-
border: 1px solid #44454b;
|
|
384
|
-
background-color: rgba(68, 69, 75, 0.2);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
.filter-card.sortable-ghost {
|
|
388
|
-
@apply opacity-50;
|
|
389
|
-
border: 1px solid #44454b;
|
|
390
|
-
background-color: rgba(68, 69, 75, 0.1);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
.filter-card.sortable-drag {
|
|
394
|
-
@apply shadow-lg transform rotate-2 scale-105;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
.expanded-filter {
|
|
398
|
-
border-left: 4px solid #44454b;
|
|
399
|
-
background-color: rgba(26, 27, 30, 0.95);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
.expanded-content {
|
|
403
|
-
background-color: rgba(26, 27, 30, 0.95);
|
|
404
|
-
border-radius: 6px;
|
|
405
|
-
padding: 12px;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
.controls-section {
|
|
409
|
-
background-color: rgba(26, 27, 30, 0.98);
|
|
410
|
-
border-radius: 6px;
|
|
411
|
-
padding: 12px;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
.excluded-filter {
|
|
415
|
-
border-left: 4px solid #EE8282;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
.normal-filter {
|
|
419
|
-
border-left: 4px solid transparent;
|
|
420
|
-
}
|
|
421
|
-
</style>
|
|
@@ -1,31 +1,35 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Modal class="overflow-y-scroll max-w-screen">
|
|
3
|
-
<template #header>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@scroll="syncScroll"
|
|
18
|
-
@input="highlightCode"
|
|
19
|
-
@keydown.tab.prevent="handleTab"></textarea>
|
|
20
|
-
</div>
|
|
21
|
-
</div>
|
|
22
|
-
<p class="text-red-400 text-bold mt-2">{{ errorMessage }}</p>
|
|
3
|
+
<template #header> Filter JSON data <FilterIcon class="ml-4" /> </template>
|
|
4
|
+
|
|
5
|
+
<div class="max-h-80 overflow-auto hidden-scrollbars z-0 my-3">
|
|
6
|
+
<CodeEditor
|
|
7
|
+
v-model="text"
|
|
8
|
+
width="100%"
|
|
9
|
+
spellcheck="false"
|
|
10
|
+
:hide_header="true"
|
|
11
|
+
:language_selector="false"
|
|
12
|
+
:languages="[['json', 'JSON']]"
|
|
13
|
+
theme="dark"
|
|
14
|
+
class=""
|
|
15
|
+
></CodeEditor>
|
|
16
|
+
<p class="text-red-400 text-bold">{{ errorMessage }}</p>
|
|
23
17
|
</div>
|
|
24
18
|
|
|
25
19
|
<div class="ml-auto flex">
|
|
26
|
-
<button
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
<button
|
|
21
|
+
class="button-default hover:opacity-70 active:opacity-50 bg-dark-400 w-48 text-xs flex items-center justify-center gap-x-2"
|
|
22
|
+
@click="save()"
|
|
23
|
+
>
|
|
24
|
+
Apply
|
|
25
|
+
</button>
|
|
26
|
+
|
|
27
|
+
<button
|
|
28
|
+
class="button-default hover:opacity-70 active:opacity-50 bg-dark-400 w-48 text-xs flex items-center justify-center gap-x-2 ml-2"
|
|
29
|
+
@click="done()"
|
|
30
|
+
>
|
|
31
|
+
Close
|
|
32
|
+
</button>
|
|
29
33
|
</div>
|
|
30
34
|
</Modal>
|
|
31
35
|
</template>
|
|
@@ -35,165 +39,20 @@
|
|
|
35
39
|
@apply flex;
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
|
-
|
|
39
|
-
/* Prism.js syntax highlighting styles */
|
|
40
|
-
.editor-container {
|
|
41
|
-
position: relative;
|
|
42
|
-
min-height: 300px;
|
|
43
|
-
max-height: 500px;
|
|
44
|
-
border-radius: 8px;
|
|
45
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
46
|
-
overflow: hidden;
|
|
47
|
-
background-color: #2e2f34;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.editor-wrapper {
|
|
51
|
-
position: relative;
|
|
52
|
-
width: 100%;
|
|
53
|
-
height: 100%;
|
|
54
|
-
min-height: 300px;
|
|
55
|
-
max-height: 500px;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.code-editor {
|
|
59
|
-
width: 100%;
|
|
60
|
-
height: 100%;
|
|
61
|
-
min-height: 300px;
|
|
62
|
-
max-height: 500px;
|
|
63
|
-
background-color: transparent;
|
|
64
|
-
/* Make text completely transparent */
|
|
65
|
-
color: rgba(0, 0, 0, 0);
|
|
66
|
-
caret-color: #e2e8f0;
|
|
67
|
-
font-family: "JetBrains Mono", "Fira Code", "Menlo", "Monaco", "Courier New", monospace;
|
|
68
|
-
padding: 12px;
|
|
69
|
-
border: none;
|
|
70
|
-
resize: none;
|
|
71
|
-
font-size: 14px;
|
|
72
|
-
line-height: 1.6;
|
|
73
|
-
tab-size: 4;
|
|
74
|
-
outline: none;
|
|
75
|
-
border: 1px solid #3d3e44;
|
|
76
|
-
border-radius: 8px;
|
|
77
|
-
z-index: 10;
|
|
78
|
-
position: absolute;
|
|
79
|
-
top: 0;
|
|
80
|
-
left: 0;
|
|
81
|
-
right: 0;
|
|
82
|
-
bottom: 0;
|
|
83
|
-
white-space: pre;
|
|
84
|
-
overflow: auto;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
.code-highlight {
|
|
88
|
-
width: 100%;
|
|
89
|
-
height: 100%;
|
|
90
|
-
min-height: 300px;
|
|
91
|
-
max-height: 500px;
|
|
92
|
-
overflow: auto;
|
|
93
|
-
white-space: pre;
|
|
94
|
-
font-family: "JetBrains Mono", "Fira Code", "Menlo", "Monaco", "Courier New", monospace;
|
|
95
|
-
font-size: 14px;
|
|
96
|
-
line-height: 1.6;
|
|
97
|
-
background-color: transparent !important;
|
|
98
|
-
pointer-events: none;
|
|
99
|
-
z-index: 5;
|
|
100
|
-
position: absolute;
|
|
101
|
-
top: 0;
|
|
102
|
-
left: 0;
|
|
103
|
-
right: 0;
|
|
104
|
-
bottom: 0;
|
|
105
|
-
padding: 12px;
|
|
106
|
-
margin: 0;
|
|
107
|
-
}
|
|
108
42
|
</style>
|
|
109
43
|
<script setup>
|
|
110
44
|
import Modal from "@/components/ui/Modal.vue";
|
|
111
45
|
import { FilterIcon } from "@/components/icons";
|
|
112
46
|
import { useUIStore } from "@/stores/ui";
|
|
113
|
-
import { ref, computed
|
|
47
|
+
import { ref, computed } from "vue";
|
|
48
|
+
import CodeEditor from "simple-code-editor";
|
|
114
49
|
|
|
115
50
|
const props = defineProps({
|
|
116
51
|
filter: Object
|
|
117
52
|
});
|
|
118
53
|
|
|
119
54
|
const ui = useUIStore();
|
|
120
|
-
const text = ref(JSON.stringify(props.filter.out(), null,
|
|
121
|
-
const codeEditor = ref(null);
|
|
122
|
-
const codeDisplay = ref(null);
|
|
123
|
-
|
|
124
|
-
// Function to highlight code using Prism
|
|
125
|
-
const highlightCode = () => {
|
|
126
|
-
if (!codeDisplay.value || !codeEditor.value) return;
|
|
127
|
-
|
|
128
|
-
// Ensure Prism is available
|
|
129
|
-
if (typeof Prism === "undefined") {
|
|
130
|
-
console.error("Prism is not loaded");
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Use requestAnimationFrame for smoother updates
|
|
135
|
-
requestAnimationFrame(() => {
|
|
136
|
-
try {
|
|
137
|
-
// Update the pre element with highlighted HTML
|
|
138
|
-
const highlighted = Prism.highlight(text.value || "", Prism.languages.json, "json");
|
|
139
|
-
codeDisplay.value.innerHTML = highlighted;
|
|
140
|
-
codeDisplay.value.className = "language-json code-highlight";
|
|
141
|
-
} catch (e) {
|
|
142
|
-
console.error("Highlight error:", e);
|
|
143
|
-
// Fallback to plain text if highlighting fails
|
|
144
|
-
codeDisplay.value.textContent = text.value || "";
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Ensure scroll positions are synced after highlighting
|
|
148
|
-
syncScroll();
|
|
149
|
-
});
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
// Function to sync scrolling between textarea and highlighted code
|
|
153
|
-
const syncScroll = () => {
|
|
154
|
-
if (!codeDisplay.value || !codeEditor.value) return;
|
|
155
|
-
|
|
156
|
-
// Synchronize scrolling between the textarea and the highlighted code
|
|
157
|
-
requestAnimationFrame(() => {
|
|
158
|
-
codeDisplay.value.scrollTop = codeEditor.value.scrollTop;
|
|
159
|
-
codeDisplay.value.scrollLeft = codeEditor.value.scrollLeft;
|
|
160
|
-
});
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
// Function to handle tab key press in the editor
|
|
164
|
-
const handleTab = (e) => {
|
|
165
|
-
const textarea = codeEditor.value;
|
|
166
|
-
const start = textarea.selectionStart;
|
|
167
|
-
const end = textarea.selectionEnd;
|
|
168
|
-
|
|
169
|
-
// Insert 4 spaces at cursor position
|
|
170
|
-
const spaces = " ";
|
|
171
|
-
text.value = text.value.substring(0, start) + spaces + text.value.substring(end);
|
|
172
|
-
|
|
173
|
-
// Move cursor position after the inserted tab
|
|
174
|
-
nextTick(() => {
|
|
175
|
-
textarea.selectionStart = textarea.selectionEnd = start + spaces.length;
|
|
176
|
-
highlightCode();
|
|
177
|
-
});
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
// Initialize syntax highlighting
|
|
181
|
-
onMounted(() => {
|
|
182
|
-
// Apply highlighting when the component is mounted
|
|
183
|
-
nextTick(() => {
|
|
184
|
-
highlightCode();
|
|
185
|
-
|
|
186
|
-
// Ensure scroll synchronization on initial load
|
|
187
|
-
if (codeEditor.value && codeDisplay.value) {
|
|
188
|
-
syncScroll();
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Focus the editor
|
|
192
|
-
if (codeEditor.value) {
|
|
193
|
-
codeEditor.value.focus();
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
});
|
|
55
|
+
const text = ref(JSON.stringify(props.filter.out(), null, "\t"));
|
|
197
56
|
|
|
198
57
|
function done() {
|
|
199
58
|
ui.toggleModal("");
|