@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.
Files changed (133) hide show
  1. package/.prettierrc +1 -27
  2. package/.vscode/extensions.json +1 -1
  3. package/README.md +79 -43
  4. package/backend/api.js +48 -40
  5. package/backend/auth.js +3 -3
  6. package/backend/batching.js +1 -1
  7. package/backend/endpoints.js +77 -13
  8. package/backend/index.js +2 -2
  9. package/backend/mock-data.js +38 -29
  10. package/backend/mock-src/classes/logger.js +8 -8
  11. package/backend/mock-src/classes/utils.js +3 -7
  12. package/backend/mock-src/database.js +0 -0
  13. package/backend/mock-src/ticketmaster.js +79 -79
  14. package/backend/validator.js +2 -2
  15. package/config/configs.json +3 -2
  16. package/config/filter.json +3 -2
  17. package/index.html +10 -81
  18. package/index.js +1 -1
  19. package/package.json +25 -40
  20. package/postcss.config.js +1 -1
  21. package/postinstall.js +17 -98
  22. package/public/android-chrome-192x192.png +0 -0
  23. package/public/android-chrome-512x512.png +0 -0
  24. package/public/apple-touch-icon.png +0 -0
  25. package/public/favicon-16x16.png +0 -0
  26. package/public/favicon-32x32.png +0 -0
  27. package/public/favicon.ico +0 -0
  28. package/public/manifest.json +7 -12
  29. package/public/sw.js +2 -0
  30. package/public/workbox-49fdaf31.js +2 -0
  31. package/public/workbox-49fdaf31.js.map +1 -0
  32. package/public/workbox-88575b92.js +2 -0
  33. package/public/workbox-88575b92.js.map +1 -0
  34. package/public/workbox-a67a7b11.js +2 -0
  35. package/public/workbox-a67a7b11.js.map +1 -0
  36. package/public/workbox-d4314735.js +2 -0
  37. package/public/workbox-d4314735.js.map +1 -0
  38. package/public/workbox-e0f89ef3.js +2 -0
  39. package/public/workbox-e0f89ef3.js.map +1 -0
  40. package/run +9 -176
  41. package/src/App.vue +85 -498
  42. package/src/assets/css/_input.scss +99 -144
  43. package/src/assets/css/main.scss +99 -450
  44. package/src/assets/img/background.svg +2 -2
  45. package/src/assets/img/logo_icon.png +0 -0
  46. package/src/components/Auth/LoginForm.vue +11 -62
  47. package/src/components/Editors/Account/Account.vue +40 -116
  48. package/src/components/Editors/Account/AccountCreator.vue +39 -88
  49. package/src/components/Editors/Account/AccountView.vue +34 -102
  50. package/src/components/Editors/Account/CreateAccount.vue +32 -80
  51. package/src/components/Editors/Profile/CreateProfile.vue +83 -269
  52. package/src/components/Editors/Profile/Profile.vue +47 -132
  53. package/src/components/Editors/Profile/ProfileCountryChooser.vue +20 -82
  54. package/src/components/Editors/Profile/ProfileView.vue +34 -91
  55. package/src/components/Editors/TagLabel.vue +6 -67
  56. package/src/components/Filter/Filter.vue +72 -289
  57. package/src/components/Filter/FilterPreview.vue +30 -171
  58. package/src/components/Filter/PriceSortToggle.vue +4 -74
  59. package/src/components/Table/Header.vue +1 -1
  60. package/src/components/Table/Row.vue +1 -1
  61. package/src/components/Table/Table.vue +2 -19
  62. package/src/components/Tasks/CheckStock.vue +13 -28
  63. package/src/components/Tasks/Controls/DesktopControls.vue +17 -17
  64. package/src/components/Tasks/Controls/MobileControls.vue +45 -8
  65. package/src/components/Tasks/CreateTaskAXS.vue +73 -79
  66. package/src/components/Tasks/CreateTaskTM.vue +142 -94
  67. package/src/components/Tasks/MassEdit.vue +7 -9
  68. package/src/components/Tasks/QuickSettings.vue +55 -169
  69. package/src/components/Tasks/ScrapeVenue.vue +4 -7
  70. package/src/components/Tasks/Stats.vue +23 -52
  71. package/src/components/Tasks/Task.vue +136 -378
  72. package/src/components/Tasks/TaskView.vue +47 -107
  73. package/src/components/Tasks/Utilities.vue +6 -5
  74. package/src/components/icons/Bag.vue +1 -1
  75. package/src/components/icons/Loyalty.vue +1 -1
  76. package/src/components/icons/Mail.vue +2 -2
  77. package/src/components/icons/Play.vue +2 -2
  78. package/src/components/icons/Reload.vue +5 -4
  79. package/src/components/icons/Sandclock.vue +2 -2
  80. package/src/components/icons/Stadium.vue +1 -1
  81. package/src/components/icons/index.js +1 -24
  82. package/src/components/ui/Modal.vue +13 -105
  83. package/src/components/ui/Navbar.vue +38 -171
  84. package/src/components/ui/ReconnectIndicator.vue +55 -351
  85. package/src/components/ui/Splash.vue +35 -5
  86. package/src/components/ui/controls/CountryChooser.vue +62 -200
  87. package/src/components/ui/controls/atomic/Checkbox.vue +10 -119
  88. package/src/components/ui/controls/atomic/Dropdown.vue +39 -208
  89. package/src/components/ui/controls/atomic/MultiDropdown.vue +37 -300
  90. package/src/libs/Filter.js +170 -200
  91. package/src/registerServiceWorker.js +1 -1
  92. package/src/stores/connection.js +53 -51
  93. package/src/stores/logger.js +3 -11
  94. package/src/stores/sampleData.js +235 -207
  95. package/src/stores/ui.js +44 -112
  96. package/src/stores/utils.js +6 -90
  97. package/src/views/Accounts.vue +35 -44
  98. package/src/views/Console.vue +90 -341
  99. package/src/views/Editor.vue +123 -1176
  100. package/src/views/FilterBuilder.vue +251 -607
  101. package/src/views/Login.vue +14 -76
  102. package/src/views/Profiles.vue +25 -44
  103. package/src/views/Tasks.vue +100 -187
  104. package/static/offline.html +50 -192
  105. package/tailwind.config.js +26 -104
  106. package/vite.config.js +16 -73
  107. package/vue.config.js +32 -0
  108. package/workbox-config.js +11 -0
  109. package/artwork/image.png +0 -0
  110. package/dev-server.js +0 -136
  111. package/exit +0 -209
  112. package/jsconfig.json +0 -16
  113. package/src/assets/css/_utilities.scss +0 -388
  114. package/src/assets/img/background.svg.backup +0 -11
  115. package/src/components/icons/Check.vue +0 -5
  116. package/src/components/icons/Close.vue +0 -21
  117. package/src/components/icons/CloseX.vue +0 -5
  118. package/src/components/icons/Key.vue +0 -21
  119. package/src/components/icons/Pencil.vue +0 -21
  120. package/src/components/icons/Profile.vue +0 -18
  121. package/src/components/icons/Sell.vue +0 -21
  122. package/src/components/icons/Spinner.vue +0 -42
  123. package/src/components/icons/SquareCheck.vue +0 -18
  124. package/src/components/icons/SquareUncheck.vue +0 -18
  125. package/src/components/icons/Wildcard.vue +0 -18
  126. package/src/components/ui/controls/atomic/LoadingButton.vue +0 -45
  127. package/src/composables/useClickOutside.js +0 -21
  128. package/src/composables/useDropdownPosition.js +0 -174
  129. package/src/types/index.js +0 -41
  130. package/src/utils/debug.js +0 -1
  131. package/switch-branch.sh +0 -41
  132. package/workbox-config.cjs +0 -63
  133. /package/src/assets/img/{logo_icon-old.png → logo_icon_2.png} +0 -0
@@ -1,125 +1,144 @@
1
1
  <template>
2
- <Row class="relative grid-cols-10 gap-2 text-white lg:grid-cols-12" @click="ui.setOpenContextMenu('')">
3
- <div class="absolute left-1 top-1 block md:hidden">
4
- <h4
5
- class="text-xs font-bold text-white"
6
- style="
7
- background: transparent !important;
8
- border: none !important;
9
- padding: 0 !important;
10
- margin: 0 !important;
11
- color: #c9cad1 !important;
12
- font-size: 11px !important;
13
- font-weight: 600 !important;
14
- letter-spacing: 0.4px !important;
15
- ">
2
+ <Row
3
+ class="relative text-white"
4
+ @click="ui.setOpenContextMenu('')"
5
+ @click.right.prevent="ui.setOpenContextMenu('')"
6
+ >
7
+ <div class="block md:hidden absolute left-1 top-0">
8
+ <h4 class="text-xs task-id text-white">
16
9
  {{ props.task.taskId }}
17
10
  </h4>
18
11
  </div>
19
- <div class="col-span-1 flex items-center justify-start lg:col-span-2">
12
+ <div class="col-span-1 lg:col-span-2 flex">
20
13
  <Checkbox
21
- class="ml-2 mr-4 flex-shrink-0"
14
+ class="ml-0 mr-4"
22
15
  :toggled="props.task.selected"
23
- @valueUpdate="ui.toggleTaskSelected(props.task.taskId)" />
24
- <h4
25
- class="task-event-id mx-auto hidden cursor-pointer text-white hover:text-light-300 lg:block"
26
- @click="copy(props.task.eventId)">
16
+ @valueUpdate="ui.toggleTaskSelected(props.task.taskId)"
17
+ />
18
+ <h4 class="task-id-alt mx-auto hidden md:block text-white" @click="copy(props.task.eventId)">
27
19
  {{ props.task.eventId }}
28
20
  </h4>
29
21
  </div>
22
+ <div class="col-span-2 hidden md:block">
23
+ <h4 class="text-white">{{ props.task.quantity }}</h4>
24
+ </div>
30
25
  <div class="col-span-2">
31
26
  <h4 class="text-white">
32
27
  <span v-if="!props.task.reservedTicketsList">-</span>
33
28
  <div v-else>
34
- <div v-for="l in props.task.reservedTicketsList.split('\n')" :key="l">
29
+ <div v-for="l in props.task.reservedTicketsList.split(' | ')" :key="l">
35
30
  <span v-if="!!l.trim()">{{ l.trim() }}</span>
36
31
  </div>
37
32
  </div>
38
33
  <span
39
34
  class="ml-1 font-bold"
40
- :class="{
41
- 'text-red-400':
42
- props.task._timeLeftString === '00:00' || props.task._timeLeftString === 'No Cartholds'
43
- }">
44
- {{ props.task._timeLeftString !== "00:00" ? props.task._timeLeftString : "Expired" }}
45
- </span>
35
+ :class="[
36
+ props.task._timeLeftString == '00:00' || props.task._timeLeftString == 'No Cartholds'
37
+ ? 'text-red-400'
38
+ : ''
39
+ ]"
40
+ >{{ props.task._timeLeftString !== "00:00" ? props.task._timeLeftString : "Expired" }}</span
41
+ >
46
42
  </h4>
47
43
  </div>
48
- <div class="col-span-5 justify-center text-center md:col-span-4 lg:col-span-5">
49
- <div class="status-container">
44
+ <div class="col-span-6 md:col-span-4 lg:col-span-3 text-center justify-center">
45
+ <div class="flex md:gap-x-2 rounded-2xl w-fit shadow-3xl mx-auto items-center justify-center bg-dark-600">
46
+ <!-- Status circle -->
50
47
  <div
51
- class="status-indicator"
52
- :class="
48
+ class="w-2 h-2 rounded-full mx-1 md:ml-3 ml-2"
49
+ :class="[
53
50
  colorToClass(
54
51
  props.task.active || props.task.status.toLowerCase() === 'idle'
55
52
  ? props.task.statusColor
56
53
  : 'red'
57
54
  )
58
- "></div>
59
- <span class="status-text">{{ props.task.status }}</span>
55
+ ]"
56
+ ></div>
57
+ <!-- Actual status -->
58
+ <span class="font-bold text-sm p-1 md:mr-3 mr-2 truncate uppercase">{{
59
+ truncate(props.task.status, statusTruncateLength)
60
+ }}</span>
60
61
  </div>
61
62
  </div>
62
- <div class="col-span-2 flex lg:col-span-3">
63
- <ul class="task-buttons">
63
+ <div class="col-span-2 flex">
64
+ <ul class="task-buttons bg-dark-600 px-1 lg:px-2 rounded-full shadow-3xl items-center">
64
65
  <li>
65
- <button v-if="task.active" @click="ui.stopTask(task.taskId)">
66
+ <button class="p-1" v-if="task.active" @click="ui.stopTask(task.taskId)">
66
67
  <PauseIcon />
67
68
  </button>
68
- <button v-else @click="ui.startTask(task.taskId)">
69
+ <button class="p-1" v-else @click="ui.startTask(task.taskId)">
69
70
  <PlayIcon />
70
71
  </button>
71
72
  </li>
72
- <li v-if="task.status?.toLowerCase() == 'waiting' && props.task._timeLeftString !== '00:00'">
73
- <button @click="ui.continueTask(task.taskId, 'autocheckout')">
73
+ <li v-if="task.status?.toLowerCase() === 'waiting' && props.task._timeLeftString !== '00:00'">
74
+ <button class="p-1" @click="ui.continueTask(task.taskId, 'autocheckout')">
74
75
  <BagWhiteIcon />
75
76
  </button>
76
77
  </li>
77
- <li v-if="task.status?.toLowerCase() == 'waiting'">
78
- <button @click="ui.continueTask(task.taskId, 'change_reservation')">
78
+ <li v-if="task.status?.toLowerCase() === 'waiting'">
79
+ <button class="p-1" @click="ui.continueTask(task.taskId, 'change_reservation')">
79
80
  <EditIcon />
80
81
  </button>
81
82
  </li>
82
83
  <li>
83
- <button @click="ui.deleteTask(task.taskId)">
84
+ <button class="p-1" @click="ui.deleteTask(task.taskId)">
84
85
  <TrashIcon />
85
86
  </button>
86
87
  </li>
87
- <li @contextmenu.prevent="handleRightClick">
88
- <button @click="props.task.isExpanded = !props.task.isExpanded">
89
- <span>{{ props.task.isExpanded ? "−" : "+" }}</span>
88
+ <li
89
+ class="text-xl -mt-2"
90
+ @click.right.prevent="window.setTimeout(() => ui.setOpenContextMenu(task.taskId), 10)"
91
+ >
92
+ <!-- eslint-disable-next-line vue/no-mutating-props -->
93
+ <button class="p-1 mt-1" @click="props.task.isExpanded = !props.task.isExpanded">
94
+ <span>{{ props.task.isExpanded ? "-" : "+" }}</span>
90
95
  </button>
91
96
  </li>
92
97
  </ul>
93
98
  </div>
94
- <div class="absolute right-5 top-4 col-span-1 hidden items-center justify-center md:block lg:flex">
95
- <h4 class="task-id text-center text-xs text-white">
99
+ <div class="hidden md:block col-span-1 absolute right-5 top-4 lg:flex items-center justify-center">
100
+ <h4 class="text-center text-xs task-id text-white">
96
101
  {{ props.task.taskId }}
97
102
  </h4>
98
103
  </div>
99
104
  <transition name="fade">
100
105
  <div
101
- class="col-span-10 flex flex-wrap gap-x-4 gap-y-4 pb-2 pt-8 will-change-auto lg:col-span-12 lg:gap-x-10 xl:justify-around"
102
- v-if="props.task.isExpanded">
106
+ class="col-span-12 flex flex-wrap gap-x-4 gap-y-4 lg:gap-x-10 pt-8 pb-2 xl:justify-around will-change-auto"
107
+ v-if="props.task.isExpanded"
108
+ >
109
+ <!-- <div class="flex gap-x-2 " @click="copy(props.task.eventId)">
110
+ <StadiumWhiteIcon />
111
+ <h4 class="text-white">{{ props.task.eventId }}</h4>
112
+ </div>
113
+ <div class="flex gap-x-2 md:hidden">
114
+ <BagWhiteIcon />
115
+ <h4 class="text-white">{{ props.task.quantity }}</h4>
116
+ </div> -->
117
+
103
118
  <!-- Details -->
104
119
  <TaskLabel
105
120
  class="md:hidden"
106
121
  image="stadium_w"
107
122
  :text="props.task.eventId"
108
- @click="copy(props.task.eventId)" />
123
+ @click="copy(props.task.eventId)"
124
+ />
109
125
 
110
- <TaskLabel class="md:hidden" image="bag_w" :text="String(props.task.quantity)" />
126
+ <TaskLabel class="md:hidden" image="bag_w" :text="props.task.quantity" />
111
127
  <TaskLabel
112
128
  v-if="props.task.email"
113
129
  image="mail"
114
130
  :text="props.task.email"
115
- @click="copy(props.task.email)" />
131
+ @click="copy(props.task.email)"
132
+ />
116
133
  <TaskLabel
117
134
  v-if="props.task.password"
118
135
  image="key"
119
136
  :text="props.task.password"
120
- @click="copy(props.task.password)" />
137
+ @click="copy(props.task.password)"
138
+ />
121
139
  <TaskLabel v-if="!props.task.email && !props.task.password" image="mail" text="No account chosen yet" />
122
140
  <TaskLabel v-if="props.task.profileName" image="profile" :text="props.task.profileName" />
141
+ <TaskLabel image="camera" :text="props.task.proxy" @click="copy(props.task.proxy)" />
123
142
  <TaskLabel image="timer" :text="props.task.smartTimer ? 'On' : 'Off'" />
124
143
  <TaskLabel image="groups" :text="props.task.loginAfterCart ? 'On' : 'Off'" />
125
144
  <TaskLabel image="hand" :text="props.task.manual ? 'On' : 'Off'" />
@@ -132,359 +151,88 @@
132
151
  v-if="props.task.presaleCode"
133
152
  @click="copy(props.task.presaleCode)"
134
153
  image="pencil"
135
- :text="props.task.presaleCode" />
154
+ :text="props.task.presaleCode"
155
+ />
136
156
 
137
157
  <TaskLabel v-if="props.task.eventName" image="stadium_w" :text="props.task.eventName" />
138
158
  <TaskLabel v-if="props.task.eventVenue" image="stadium_w" :text="props.task.eventVenue" />
139
159
  <TaskLabel
140
160
  v-if="props.task.eventLocalDate"
141
161
  image="stadium_w"
142
- :text="formatDate(props.task.eventLocalDate)" />
162
+ :text=" formatDate(props.task.eventLocalDate)"
163
+ />
143
164
  <TaskLabel image="sandclock" :text="props.task.agedAccount ? 'On' : 'Off'" />
144
165
  </div>
145
166
  </transition>
146
167
 
147
168
  <!-- Context menu -->
148
- <transition name="fade">
149
- <div
150
- v-if="ui.openContextMenu === task.taskId"
151
- ref="contextMenuRef"
152
- class="w-42 context-menu z-max fixed grid grid-cols-1 gap-1 rounded-lg border border-dark-650 bg-dark-500 p-1 text-white shadow-xl"
153
- :style="contextMenuPosition">
154
- <button class="btn-primary" @click="openInNewTab(`${ui.currentCountry.url}/event/${task.eventId}`)">
155
- Open Event
156
- </button>
157
- <button v-if="task.openerLink" class="btn-primary" @click="openInBrowser(false)">
158
- Open in browser (proxy)
159
- </button>
160
- <button v-if="task.openerLink" class="btn-primary" @click="openInBrowser(true)">
161
- Open in browser (debug)
162
- </button>
163
- </div>
164
- </transition>
169
+
170
+ <div class="absolute -bottom-1.5 right-5">
171
+ <transition name="fade">
172
+ <div
173
+ v-if="ui.openContextMenu === task.taskId"
174
+ class="bg-light-300 text-white w-42 grid grid-cols-1 p-1 gap-1 z-50 rounded-lg shadow-xl relative"
175
+ >
176
+ <!-- <span class="text-light-400">{{ task.taskId }}</span> -->
177
+ <button
178
+ class="bg-dark-500 smooth-hover p-1 rounded-lg"
179
+ @click="openInNewTab(`${ui.currentCountry.url}/event/${task.eventId}`)"
180
+ >
181
+ Open Event
182
+ </button>
183
+ <button
184
+ v-if="task.openerLink"
185
+ class="bg-dark-500 smooth-hover p-1 rounded-lg"
186
+ @click="openInBrowser(false)"
187
+ >
188
+ Open in browser (proxy)
189
+ </button>
190
+ <button
191
+ v-if="task.openerLink"
192
+ class="bg-dark-500 smooth-hover p-1 rounded-lg"
193
+ @click="openInBrowser(true)"
194
+ >
195
+ Open in browser (debug)
196
+ </button>
197
+ </div>
198
+ </transition>
199
+ </div>
165
200
  </Row>
166
201
  </template>
167
202
  <style lang="scss" scoped>
168
203
  h4 {
169
204
  @apply text-center;
170
- color: #e2e2e5;
171
- }
172
-
173
- .status-container {
174
- @apply mx-auto flex w-fit items-center justify-center rounded-lg border border-dark-650 bg-dark-500;
175
- padding: 6px 12px;
176
- gap: 6px;
177
- transition: all 0.15s ease;
178
- max-width: 100%;
179
-
180
- &:hover {
181
- @apply border-dark-700 bg-dark-550;
182
- }
183
- }
184
-
185
- .status-text {
186
- @apply truncate text-sm font-medium uppercase;
187
- color: #e2e2e5;
188
- letter-spacing: 0.025em;
189
205
  }
190
206
 
191
- .task-buttons {
192
- @apply mx-auto flex items-center justify-center rounded border border-dark-650 bg-dark-500;
193
- padding: 2px;
194
- gap: 1px;
195
-
196
- button {
197
- @apply relative flex items-center justify-center rounded border-0 outline-0 transition-all duration-150;
198
- background: transparent;
199
- width: 22px;
200
- height: 22px;
201
- color: #cccccc;
202
- border-radius: 4px;
203
-
204
- &:hover {
205
- background: rgba(255, 255, 255, 0.1);
206
- color: #ffffff;
207
- transform: scale(1.05);
208
- }
209
-
210
- &:active {
211
- background: rgba(255, 255, 255, 0.2);
212
- transform: scale(0.95);
213
- }
214
- }
215
-
216
- svg,
217
- img {
218
- width: 12px;
219
- height: 12px;
220
- position: relative;
221
- z-index: 1;
222
- }
223
-
224
- svg path {
225
- fill: currentColor;
226
- }
227
-
228
- span {
229
- @apply relative z-[1] text-xs font-medium;
207
+ @media (max-width: 1024px) {
208
+ h4 {
209
+ font-size: 10px !important;
230
210
  }
231
- }
232
-
233
- /* Tablet sizing - medium buttons */
234
- @media (min-width: 768px) and (max-width: 1023px) {
235
211
  .task-buttons {
236
- padding: 2px;
237
- gap: 1px;
238
- border-radius: 6px;
239
-
240
- button {
241
- width: 26px;
242
- height: 26px;
243
- border-radius: 5px;
244
- }
245
-
246
- svg,
247
- img {
248
- width: 14px;
249
- height: 14px;
250
- }
251
-
252
- span {
253
- font-size: 0.75rem;
254
- }
212
+ @apply gap-x-3;
255
213
  }
256
- }
257
-
258
- /* Desktop sizing - large buttons */
259
- @media (min-width: 1024px) {
260
- .task-buttons {
261
- padding: 3px;
262
- gap: 2px;
263
- border-radius: 8px;
264
-
265
- button {
266
- width: 28px;
267
- height: 28px;
268
- border-radius: 6px;
269
- }
270
-
271
- svg,
272
- img {
273
- width: 16px;
274
- height: 16px;
275
- }
276
-
277
- span {
278
- font-size: 0.875rem;
279
- }
280
- }
281
- }
282
-
283
- /* Mobile portrait specific styling */
284
- @media (max-width: 480px) and (orientation: portrait) {
285
- /* Position adjustment for mobile taskId */
286
- .block.md\\:hidden {
287
- left: 4rem !important;
288
- top: 0.25rem !important;
289
- z-index: 1 !important;
290
- }
291
-
292
- /* Improved button layout for mobile portrait */
293
- .task-buttons {
294
- padding: 1px;
295
- gap: 1px;
296
- border-radius: 4px;
297
- border: 1px solid #3d3e44 !important;
298
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important;
299
- max-width: 100%;
300
- min-height: 28px;
301
- height: auto;
302
- flex-wrap: wrap;
303
- justify-content: center;
304
- align-items: center;
305
- background: #2e2f34;
306
-
307
- button {
308
- width: 20px;
309
- height: 20px;
310
- border-radius: 3px;
311
- min-width: 20px;
312
- border: none !important;
313
- flex-shrink: 0;
314
- margin: 0.5px;
315
- background: transparent;
316
-
317
- &:hover {
318
- background: rgba(255, 255, 255, 0.1);
319
- transform: scale(1.05);
320
- }
321
-
322
- &:active {
323
- background: rgba(255, 255, 255, 0.15);
324
- transform: scale(0.95);
325
- }
326
- }
327
-
328
- svg,
329
- img {
330
- width: 10px;
331
- height: 10px;
332
- }
333
-
334
- span {
335
- font-size: 0.7rem;
336
- line-height: 1;
337
- }
338
-
339
- /* Improved list item layout */
340
- li {
341
- display: flex;
342
- margin: 0;
343
- padding: 0;
344
- }
345
- }
346
-
347
- /* Make the actions column more flexible */
348
- .col-span-2.lg\\:col-span-3 {
349
- min-width: 0;
350
- flex-shrink: 0;
351
- display: flex;
352
- align-items: center;
353
- justify-content: center;
354
- padding: 0 2px;
355
- }
356
-
357
- /* Compact status container */
358
- .status-container {
359
- border: none !important;
360
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important;
361
- padding: 2px 4px;
362
- font-size: 0.7rem;
363
- max-width: 100%;
364
- }
365
-
366
- .status-text {
367
- font-size: 0.65rem;
368
- letter-spacing: 0;
369
- }
370
-
371
- /* Reduce row height to accommodate smaller elements */
372
- .task-row-container {
373
- min-height: 45px !important;
374
- }
375
- }
376
-
377
- .task-id {
378
- font-size: 10px;
379
- font-weight: 600;
380
- letter-spacing: 0.5px;
381
- margin: 0;
382
- color: #a0a0a6;
383
- background: rgba(46, 47, 52, 0.4);
384
- padding: 2px 6px;
385
- border-radius: 4px;
386
- border: 1px solid #3d3e44;
387
- }
388
-
389
- .task-event-id {
390
- font-size: 11px;
391
- font-weight: 500;
392
- text-align: center;
393
- color: #d0d0d3;
394
-
395
- &:hover {
396
- color: #ffffff;
397
- }
398
-
399
- @media (max-width: 1024px) {
400
- font-size: 10px;
401
- }
402
-
403
- @media (max-width: 768px) {
404
- font-size: 9px;
405
- }
406
- }
407
-
408
- /* Responsive task styling */
409
- @screen lg {
410
- h4 {
411
- font-size: 10px;
412
- }
413
-
414
214
  .task-id {
415
- font-size: 8px;
215
+ font-size: 6px !important;
416
216
  }
417
-
418
- .task-event-id {
419
- font-size: 10px;
217
+ .task-id-alt {
218
+ font-size: 7px !important;
420
219
  }
421
220
  }
422
221
  </style>
423
-
424
222
  <script setup>
425
- /// <reference path="@/types/index.js" />
426
-
427
223
  import { Row } from "@/components/Table";
428
224
  import { PlayIcon, TrashIcon, BagWhiteIcon, PauseIcon, EditIcon } from "@/components/icons";
429
225
  import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
430
226
  import { useUIStore } from "@/stores/ui";
431
227
  import TaskLabel from "@/components/Tasks/TaskLabel.vue";
432
- import { computed, ref, onMounted, onUnmounted, nextTick } from "vue";
228
+ import { ref } from "vue";
433
229
 
434
230
  const ui = useUIStore();
435
231
 
436
- /** @type {{ task: Task }} */
437
232
  const props = defineProps({
438
233
  task: { type: Object }
439
234
  });
440
235
 
441
- // Context menu positioning
442
- const contextMenuPosition = ref({});
443
- const contextMenuRef = ref(null);
444
-
445
- // Handle right-click to position context menu
446
- const handleRightClick = (event) => {
447
- const menuWidth = 168; // w-42 = 10.5rem = 168px
448
- const menuHeight = 200; // Approximate height
449
-
450
- let x = event.clientX;
451
- let y = event.clientY - 55;
452
-
453
- // Prevent menu from going off screen
454
- if (x + menuWidth > window.innerWidth) {
455
- x = event.clientX - menuWidth; // Show to the left instead
456
- }
457
- if (y + menuHeight > window.innerHeight) {
458
- y = event.clientY - menuHeight; // Show above instead
459
- }
460
-
461
- contextMenuPosition.value = {
462
- left: `${x}px`,
463
- top: `${y}px`
464
- };
465
-
466
- // Open the context menu for this task
467
- ui.setOpenContextMenu(props.task.taskId);
468
-
469
- // Add click outside listener after menu opens
470
- nextTick(() => {
471
- document.addEventListener("click", handleClickOutside);
472
- });
473
- };
474
-
475
- // Handle clicking outside the context menu
476
- const handleClickOutside = (event) => {
477
- if (contextMenuRef.value && !contextMenuRef.value.contains(event.target)) {
478
- ui.setOpenContextMenu("");
479
- document.removeEventListener("click", handleClickOutside);
480
- }
481
- };
482
-
483
- // Cleanup on unmount
484
- onUnmounted(() => {
485
- document.removeEventListener("click", handleClickOutside);
486
- });
487
-
488
236
  const copy = (txt) => {
489
237
  if (!txt) return;
490
238
  navigator.clipboard.writeText(txt);
@@ -500,29 +248,28 @@ const colorToClass = (color) => {
500
248
  return colorMapping.get(color) || "bg-white";
501
249
  };
502
250
 
251
+ const truncate = (text, after) => {
252
+ if (text?.length <= after || after === -1) return text;
253
+ return text?.substring(0, after) + "...";
254
+ };
255
+
503
256
  const openInBrowser = (debug) => {
504
257
  if (!props.task.openerLink) return;
505
258
  ui.showSuccess(`Opening in browser ${debug ? "(debug)" : ""}`);
506
259
  const input = props.task.openerLink;
507
- const data = JSON.parse(atob(input.split("://").pop()));
260
+ const data = JSON.parse(atob(input.split("://")[1]));
508
261
  data.config.debug = debug;
509
- const out = "necro://" + btoa(JSON.stringify(data));
262
+ const out = "russonoro://" + btoa(JSON.stringify(data));
510
263
  openInNewTab(out);
511
264
  };
512
265
 
513
266
  const formatDate = (date) => {
514
- if (!date) return "TBA";
267
+ if (!date) return "-";
515
268
  const d = new Date(date);
516
- const iso = d.toISOString();
517
- const [year, month, day] = iso.substring(0, 10).split("-");
518
- const time = iso.substring(11, 16);
519
- const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
520
- const dayName = dayNames[d.getUTCDay()];
521
- const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
522
- const monthName = monthNames[parseInt(month) - 1];
523
- return `${dayName} ${monthName} ${parseInt(day)} ${year} ${time}`;
269
+ return `${d.toDateString()} ${d.toTimeString().split(":").slice(0, 2).join(":")}`;
524
270
  };
525
271
 
272
+
526
273
  const openInNewTab = (href) => {
527
274
  if (!href) return;
528
275
  ui.logger.Info("Opening", href);
@@ -532,4 +279,15 @@ const openInNewTab = (href) => {
532
279
  href: href
533
280
  }).click();
534
281
  };
282
+
283
+ const getMaxStatusLength = (width) => {
284
+ if (width > 1279) return -1;
285
+ if (width > 767) return 25;
286
+ if (width > 639) return 30;
287
+ if (width > 540) return 18;
288
+ return 13;
289
+ };
290
+
291
+ let statusTruncateLength = ref(getMaxStatusLength(window.innerWidth));
292
+ window.addEventListener("resize", () => (statusTruncateLength.value = getMaxStatusLength(window.innerWidth)));
535
293
  </script>