@necrolab/dashboard 0.5.22 → 0.5.24
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/package.json +4 -4
- package/src/components/Editors/Account/Account.vue +1 -1
- package/src/components/Editors/Profile/CreateProfile.vue +11 -11
- package/src/components/Filter/Filter.vue +13 -0
- package/src/components/Tasks/Task.vue +63 -26
- package/src/components/ui/StatusBadge.vue +1 -1
- package/src/composables/useFilterCSS.js +6 -12
- package/src/libs/Filter.js +241 -21
- package/src/libs/tm-renderer/axs/renderer.js +97 -0
- package/src/libs/tm-renderer/axs/requests.js +37 -0
- package/src/libs/tm-renderer/base-renderer.js +93 -0
- package/src/libs/tm-renderer/dependencies/logger.js +186 -0
- package/src/libs/tm-renderer/dependencies/web.persist.js +64 -0
- package/src/libs/tm-renderer/factory.js +40 -0
- package/src/libs/tm-renderer/index.js +3 -0
- package/src/libs/tm-renderer/request-utils.js +85 -0
- package/src/libs/tm-renderer/tm/renderer.js +568 -0
- package/src/libs/tm-renderer/tm/requests.js +47 -0
- package/src/libs/tm-renderer/tm/tm-utils.js +40 -0
- package/src/libs/tm-renderer/utils.js +90 -0
- package/src/views/FilterBuilder.vue +163 -57
- package/vite.config.js +1 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@necrolab/dashboard",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.24",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "rm -rf dist && vite build && npx workbox-cli generateSW workbox-config.cjs",
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
"bot": "node dev-server.js",
|
|
9
9
|
"expose": "node dev-server.js",
|
|
10
10
|
"postinstall": "node postinstall.js",
|
|
11
|
-
"updaterenderer": "npm i @necrolab/tm-renderer@latest",
|
|
12
11
|
"preview": "vite preview",
|
|
13
|
-
"lint": "npx eslint src/ --fix"
|
|
12
|
+
"lint": "npx eslint src/ --fix",
|
|
13
|
+
"release": "npm version patch && npm i && npm publish",
|
|
14
|
+
"release:minor": "npm version minor && npm i && npm publish"
|
|
14
15
|
},
|
|
15
16
|
"dependencies": {
|
|
16
17
|
"@msgpack/msgpack": "^3.0.0-beta2",
|
|
17
|
-
"@necrolab/tm-renderer": "^0.1.12",
|
|
18
18
|
"@vitejs/plugin-vue": "^5.2.1",
|
|
19
19
|
"@vueuse/core": "^11.3.0",
|
|
20
20
|
"autoprefixer": "^10.4.21",
|
|
@@ -8,8 +8,17 @@
|
|
|
8
8
|
|
|
9
9
|
<div>
|
|
10
10
|
<div class="my-3 grid grid-cols-12 gap-3">
|
|
11
|
+
<!-- Country chooser -->
|
|
12
|
+
<FormField label="Country" :icon="StadiumIcon" z-index="0" class="col-span-4 md:col-span-2" noWrapper>
|
|
13
|
+
<ProfileCountryChooser
|
|
14
|
+
class="h-10"
|
|
15
|
+
:value="formProfile.country"
|
|
16
|
+
:onClick="chooseCountry"
|
|
17
|
+
:disabled="true" />
|
|
18
|
+
</FormField>
|
|
19
|
+
|
|
11
20
|
<!-- Profile tag -->
|
|
12
|
-
<FormField label="Profile Tag" :icon="TagIcon" z-index="0" class="col-span-
|
|
21
|
+
<FormField label="Profile Tag" :icon="TagIcon" z-index="0" class="col-span-8 md:col-span-4" noWrapper>
|
|
13
22
|
<Dropdown
|
|
14
23
|
:class="`input-default dropdown w-full`"
|
|
15
24
|
:default="ui.profile.tags[0]"
|
|
@@ -19,7 +28,7 @@
|
|
|
19
28
|
</FormField>
|
|
20
29
|
|
|
21
30
|
<!-- Card Number -->
|
|
22
|
-
<FormField label="Card Number" :icon="CartIcon" :error="errors.includes('cardNumber')" z-index="0" class="col-span-12 md:col-span-
|
|
31
|
+
<FormField label="Card Number" :icon="CartIcon" :error="errors.includes('cardNumber')" z-index="0" class="col-span-12 md:col-span-6">
|
|
23
32
|
<input
|
|
24
33
|
ref="cardNumberInput"
|
|
25
34
|
placeholder="Enter card number"
|
|
@@ -30,15 +39,6 @@
|
|
|
30
39
|
@focus="formatCardNumberDisplay" />
|
|
31
40
|
</FormField>
|
|
32
41
|
|
|
33
|
-
<!-- Country chooser -->
|
|
34
|
-
<FormField label="Country" :icon="StadiumIcon" z-index="0" class="col-span-12 md:col-span-2" noWrapper>
|
|
35
|
-
<ProfileCountryChooser
|
|
36
|
-
class="h-10"
|
|
37
|
-
:value="formProfile.country"
|
|
38
|
-
:onClick="chooseCountry"
|
|
39
|
-
:disabled="true" />
|
|
40
|
-
</FormField>
|
|
41
|
-
|
|
42
42
|
<!-- Exp Year -->
|
|
43
43
|
<FormField label="Expiry Year" :icon="TimerIcon" :error="errors.includes('expYear')" z-index="0" class="col-span-6 md:col-span-5" noWrapper>
|
|
44
44
|
<Dropdown
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
<div class="grid grid-cols-12 items-center py-3 px-2 sm:px-4 gap-2 sm:gap-3">
|
|
11
11
|
<div class="col-span-9 sm:col-span-10">
|
|
12
12
|
<div class="flex-gap-2 items-center sm:gap-3 cursor-pointer flex-1" @click="handleFilterClick(filter.id)">
|
|
13
|
+
<div
|
|
14
|
+
class="filter-color-dot flex-shrink-0"
|
|
15
|
+
:style="{ backgroundColor: color }">
|
|
16
|
+
</div>
|
|
13
17
|
<div class="filter-type-badge flex-shrink-0">
|
|
14
18
|
<component :is="getFilterIcon()" class="w-3 h-3 sm:w-4 sm:h-4" />
|
|
15
19
|
</div>
|
|
@@ -169,6 +173,10 @@ const props = defineProps({
|
|
|
169
173
|
filterBuilder: {
|
|
170
174
|
type: Object,
|
|
171
175
|
required: true
|
|
176
|
+
},
|
|
177
|
+
color: {
|
|
178
|
+
type: String,
|
|
179
|
+
default: "#7bc999"
|
|
172
180
|
}
|
|
173
181
|
});
|
|
174
182
|
|
|
@@ -307,6 +315,11 @@ props.filterBuilder.onUpdate(() => {
|
|
|
307
315
|
@apply border-t border-t-dark-625/20;
|
|
308
316
|
}
|
|
309
317
|
|
|
318
|
+
.filter-color-dot {
|
|
319
|
+
@apply w-2.5 h-2.5 rounded-full flex-shrink-0;
|
|
320
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
321
|
+
}
|
|
322
|
+
|
|
310
323
|
.filter-type-badge {
|
|
311
324
|
@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
325
|
}
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Row
|
|
3
|
-
class="relative grid-cols-10 gap-2 text-white lg:grid-cols-12
|
|
3
|
+
class="relative min-h-full grid-cols-10 gap-2 text-white lg:grid-cols-12"
|
|
4
4
|
@click="ui.setOpenContextMenu('')"
|
|
5
5
|
@dblclick="handleDoubleClick"
|
|
6
6
|
@touchstart="handleTouchStart"
|
|
7
7
|
@touchend="handleTouchEnd">
|
|
8
|
-
<div class="col-span-1 flex items-center justify-start lg:col-span-2
|
|
8
|
+
<div class="col-span-1 flex items-center justify-start py-2 lg:col-span-2">
|
|
9
9
|
<Checkbox
|
|
10
10
|
class="ml-2 mr-4 flex-shrink-0"
|
|
11
11
|
:toggled="props.task.selected"
|
|
12
12
|
@valueUpdate="ui.toggleTaskSelected(props.task.taskId)" />
|
|
13
13
|
<div
|
|
14
14
|
v-if="props.preferEventName && props.task.eventName"
|
|
15
|
-
class="hidden
|
|
15
|
+
class="hidden min-w-0 max-w-full cursor-pointer flex-col justify-center gap-0.5 lg:flex"
|
|
16
16
|
@click="copy(props.task.eventId, 'Copied event ID')"
|
|
17
17
|
:title="`Event ID: ${props.task.eventId}`">
|
|
18
|
-
<div class="max-w-45
|
|
18
|
+
<div class="max-w-45 text-xs+ truncate font-semibold leading-tight text-white">
|
|
19
19
|
{{ props.task.eventName }}
|
|
20
20
|
</div>
|
|
21
|
-
<EventDetailRow
|
|
21
|
+
<EventDetailRow
|
|
22
|
+
:content="[props.task.venueName, props.task.eventCity].filter(Boolean).join(', ')"
|
|
23
|
+
truncate>
|
|
22
24
|
<template #icon>
|
|
23
25
|
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
|
|
24
26
|
<circle cx="12" cy="10" r="3"></circle>
|
|
@@ -32,26 +34,46 @@
|
|
|
32
34
|
<line x1="3" y1="10" x2="21" y2="10"></line>
|
|
33
35
|
</template>
|
|
34
36
|
</EventDetailRow>
|
|
35
|
-
<div v-if="props.task.email" class="
|
|
36
|
-
<svg
|
|
37
|
+
<div v-if="props.task.email" class="text-3xs leading-tight-sm min-h-2.75 flex items-start gap-1">
|
|
38
|
+
<svg
|
|
39
|
+
class="icon-sm"
|
|
40
|
+
width="10"
|
|
41
|
+
height="10"
|
|
42
|
+
fill="none"
|
|
43
|
+
stroke="currentColor"
|
|
44
|
+
stroke-width="2"
|
|
45
|
+
viewBox="0 0 24 24">
|
|
37
46
|
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
|
|
38
47
|
<polyline points="22,6 12,13 2,6"></polyline>
|
|
39
48
|
</svg>
|
|
40
|
-
<span class="
|
|
49
|
+
<span class="text-3xs leading-tight-sm truncate text-light-500">{{ props.task.email }}</span>
|
|
41
50
|
</div>
|
|
42
51
|
</div>
|
|
43
52
|
<div
|
|
44
53
|
v-else
|
|
45
|
-
class="hidden
|
|
46
|
-
@click="
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
class="hidden min-w-0 max-w-full cursor-pointer flex-col justify-center gap-0.5 lg:flex"
|
|
55
|
+
@click="
|
|
56
|
+
copy(
|
|
57
|
+
props.task.eventId || props.task.email,
|
|
58
|
+
props.task.eventId ? 'Copied event ID' : 'Copied email'
|
|
59
|
+
)
|
|
60
|
+
">
|
|
61
|
+
<div
|
|
62
|
+
class="flex-gap-1 text-xs+ max-w-45 leading-tight-md items-center truncate font-semibold text-white">
|
|
63
|
+
<svg
|
|
64
|
+
class="icon-sm"
|
|
65
|
+
width="11"
|
|
66
|
+
height="11"
|
|
67
|
+
fill="none"
|
|
68
|
+
stroke="currentColor"
|
|
69
|
+
stroke-width="2.5"
|
|
70
|
+
viewBox="0 0 24 24">
|
|
49
71
|
<path d="M9 11l3 3L22 4"></path>
|
|
50
72
|
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path>
|
|
51
73
|
</svg>
|
|
52
74
|
<span>{{ props.task.eventId || props.task.email }}</span>
|
|
53
75
|
</div>
|
|
54
|
-
<EventDetailRow v-if="props.task.eventId
|
|
76
|
+
<EventDetailRow v-if="!props.task.eventId" :content="props.task.email" truncate>
|
|
55
77
|
<template #icon>
|
|
56
78
|
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
|
|
57
79
|
<polyline points="22,6 12,13 2,6"></polyline>
|
|
@@ -60,19 +82,29 @@
|
|
|
60
82
|
</div>
|
|
61
83
|
</div>
|
|
62
84
|
<div class="col-span-2 overflow-hidden lg:col-span-3 xl:col-span-2">
|
|
63
|
-
<h4 class="text-
|
|
85
|
+
<h4 class="lg:text-2xs text-center text-xs leading-tight text-light-300">
|
|
64
86
|
<span v-if="!props.task.reservedTicketsList">-</span>
|
|
65
87
|
<div v-else class="overflow-hidden">
|
|
66
|
-
<div
|
|
88
|
+
<div
|
|
89
|
+
v-for="(l, index) in props.task.reservedTicketsList.split('\n')"
|
|
90
|
+
:key="`ticket-${index}`"
|
|
91
|
+
class="truncate">
|
|
67
92
|
<span
|
|
68
93
|
v-if="!!l.trim()"
|
|
69
94
|
class="text-xs"
|
|
70
|
-
:class="{
|
|
71
|
-
|
|
95
|
+
:class="{
|
|
96
|
+
'font-bold text-green-400': isTotalPrice(
|
|
97
|
+
l,
|
|
98
|
+
index,
|
|
99
|
+
props.task.reservedTicketsList.split(' ')
|
|
100
|
+
)
|
|
101
|
+
}">
|
|
102
|
+
{{ l.trim() }}
|
|
103
|
+
</span>
|
|
72
104
|
</div>
|
|
73
105
|
</div>
|
|
74
106
|
<span
|
|
75
|
-
class="
|
|
107
|
+
class="mt-1 block text-xs font-bold"
|
|
76
108
|
:class="{
|
|
77
109
|
'text-red-400':
|
|
78
110
|
props.task._timeLeftString === '00:00' || props.task._timeLeftString === 'No Cartholds'
|
|
@@ -84,7 +116,7 @@
|
|
|
84
116
|
<div class="col-span-6 justify-center text-center md:col-span-5 lg:col-span-4 xl:col-span-5">
|
|
85
117
|
<div class="task-status-badge">
|
|
86
118
|
<div
|
|
87
|
-
class="
|
|
119
|
+
class="h-2 w-2 flex-shrink-0 rounded-full"
|
|
88
120
|
:class="
|
|
89
121
|
colorToClass(
|
|
90
122
|
props.task.active || props.task.status.toLowerCase() === 'idle'
|
|
@@ -92,10 +124,14 @@
|
|
|
92
124
|
: 'red'
|
|
93
125
|
)
|
|
94
126
|
"></div>
|
|
95
|
-
<span
|
|
127
|
+
<span
|
|
128
|
+
class="max-md:text-xs max-md:max-w-full max-sm:tracking-normal truncate text-sm font-medium uppercase tracking-wide text-light-300">
|
|
129
|
+
{{ props.task.status }}
|
|
130
|
+
</span>
|
|
96
131
|
</div>
|
|
97
132
|
</div>
|
|
98
|
-
<div
|
|
133
|
+
<div
|
|
134
|
+
class="max-sm:min-w-0 max-sm:flex-shrink-0 max-sm:items-center max-sm:justify-end max-sm:px-0.5 col-span-1 flex overflow-visible md:col-span-2 lg:col-span-3">
|
|
99
135
|
<ActionButtonGroup class="overflow-visible" :allowWrap="true">
|
|
100
136
|
<li>
|
|
101
137
|
<button v-if="task.active" @click="ui.stopTask(task.taskId)" aria-label="Stop task">
|
|
@@ -127,8 +163,10 @@
|
|
|
127
163
|
</li>
|
|
128
164
|
</ActionButtonGroup>
|
|
129
165
|
</div>
|
|
130
|
-
<div
|
|
131
|
-
|
|
166
|
+
<div
|
|
167
|
+
class="max-sm:left-16 max-sm:top-1 max-sm:z-10 absolute right-5 top-4 col-span-1 hidden items-center justify-center xl:flex">
|
|
168
|
+
<h4
|
|
169
|
+
class="bg-dark-475/40 text-2xs lg:text-2xs m-0 rounded border border-dark-500 px-1.5 py-0.5 text-center text-xs font-semibold tracking-wide text-light-500">
|
|
132
170
|
{{ props.task.taskId }}
|
|
133
171
|
</h4>
|
|
134
172
|
</div>
|
|
@@ -137,7 +175,7 @@
|
|
|
137
175
|
<div
|
|
138
176
|
v-if="ui.openContextMenu === task.taskId"
|
|
139
177
|
ref="contextMenuRef"
|
|
140
|
-
class="w-42 context-menu z-max
|
|
178
|
+
class="w-42 context-menu fixed z-max grid grid-cols-1 gap-1 rounded-lg border border-dark-650 bg-dark-500 p-1 text-white shadow-xl"
|
|
141
179
|
:style="contextMenuPosition">
|
|
142
180
|
<button class="btn-primary" @click="openInNewTab(`${ui.currentCountry.url}/event/${task.eventId}`)">
|
|
143
181
|
Open Event
|
|
@@ -237,10 +275,9 @@ onUnmounted(() => {
|
|
|
237
275
|
document.removeEventListener("click", handleClickOutside);
|
|
238
276
|
});
|
|
239
277
|
|
|
240
|
-
|
|
241
278
|
const openViewTaskModal = () => {
|
|
242
279
|
ui.selectedTaskForView = props.task;
|
|
243
|
-
ui.toggleModal(
|
|
280
|
+
ui.toggleModal("view-task");
|
|
244
281
|
};
|
|
245
282
|
|
|
246
283
|
const openInBrowser = (debug) => {
|
|
@@ -25,7 +25,7 @@ const disabledClasses = "bg-red-500/20 border-red-500/30";
|
|
|
25
25
|
<template>
|
|
26
26
|
<div
|
|
27
27
|
:class="[
|
|
28
|
-
'flex-center rounded-full border-2 transition-all duration-200',
|
|
28
|
+
'flex items-center justify-center rounded-full border-2 transition-all duration-200',
|
|
29
29
|
enabled ? enabledClasses : disabledClasses,
|
|
30
30
|
sizeClasses[size]
|
|
31
31
|
]">
|
|
@@ -19,7 +19,9 @@ export function useFilterCSS(filterBuilder, svg) {
|
|
|
19
19
|
document.head.appendChild(styleElement);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
// Only use cssClasses now (permanent filter colors)
|
|
23
|
+
// temporaryCSS is no longer used - hover uses DOM classes instead
|
|
24
|
+
const combinedCSS = filterBuilder.value.cssClasses;
|
|
23
25
|
if (styleElement.textContent !== combinedCSS) {
|
|
24
26
|
styleElement.textContent = combinedCSS;
|
|
25
27
|
}
|
|
@@ -35,17 +37,9 @@ export function useFilterCSS(filterBuilder, svg) {
|
|
|
35
37
|
});
|
|
36
38
|
};
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
nextTick(() => injectStyles());
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const originalClearHighlight = filterBuilder.value.clearHighlight.bind(filterBuilder.value);
|
|
45
|
-
filterBuilder.value.clearHighlight = () => {
|
|
46
|
-
originalClearHighlight();
|
|
47
|
-
nextTick(() => injectStyles());
|
|
48
|
-
};
|
|
40
|
+
// NOTE: highlight() and clearHighlight() now use DOM class manipulation
|
|
41
|
+
// They don't modify CSS, so we don't wrap them to inject styles
|
|
42
|
+
// This eliminates the flicker caused by style element updates
|
|
49
43
|
};
|
|
50
44
|
|
|
51
45
|
const cleanupStyles = () => {
|