@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,64 +1,64 @@
1
1
  <template>
2
2
  <Row
3
- class="relative grid-cols-7 text-white lg:grid-cols-8"
3
+ class="relative text-white grid-cols-7 lg:grid-cols-8"
4
4
  @click="ui.setOpenContextMenu('')"
5
- @click.right.prevent="ui.setOpenContextMenu('')">
6
- <div class="col-span-3 flex lg:col-span-2">
5
+ @click.right.prevent="ui.setOpenContextMenu('')"
6
+ >
7
+ <div class="col-span-3 lg:col-span-2 flex">
7
8
  <Checkbox
8
9
  class="ml-0 mr-4"
9
- :toggled="props.profile.selected"
10
- @valueUpdate="ui.toggleProfileSelected(props.profile.id)" />
10
+ :toggled="props.task.selected"
11
+ @valueUpdate="ui.toggleProfileSelected(props.task._id)"
12
+ />
11
13
  <h4 class="mx-auto text-white">
12
- {{ props.profile.profileName }}
14
+ {{ props.task.profileName }}
13
15
  </h4>
14
16
  </div>
15
17
  <div class="col-span-1 lg:col-span-2">
16
- <h4 class="flex items-center justify-center gap-2 text-white">
17
- <span class="hidden sm:block">
18
- {{
19
- props.profile.privacy
20
- ? props.profile.cardNumber[0] +
21
- "•".repeat(props.profile.cardNumber.length - 5) +
22
- props.profile.cardNumber.slice(-4)
23
- : validateCard(props.profile.cardNumber).formatted
24
- }}
25
- </span>
26
- <img class="h-6 w-6" :src="getAccountType()" />
18
+ <h4 class="text-white flex justify-center items-center gap-2">
19
+ <span class="hidden ipadlg:block">{{
20
+ props.task.privacy
21
+ ? props.task.cardNumber[0] +
22
+ "•".repeat(props.task.cardNumber.length - 5) +
23
+ props.task.cardNumber.slice(-4)
24
+ : validateCard(props.task.cardNumber).formatted
25
+ }}</span>
26
+ <img width="22 px" :src="getAccountType()" />
27
27
  </h4>
28
28
  </div>
29
29
  <div class="col-span-1">
30
30
  <h4 class="text-white">{{ expDate() }}</h4>
31
31
  </div>
32
32
  <div class="col-span-1">
33
- <h4 v-if="props.profile.enabled" class="flex justify-center text-green-400">
34
- <img class="green h-3 w-3" src="/img/controls/enable.svg" />
33
+ <h4 v-if="props.task.enabled" class="text-green-400 flex justify-center">
34
+ <img width="12px" height="12px" class="green" src="/img/controls/enable.svg" />
35
35
  </h4>
36
- <h4 v-else class="flex justify-center text-red-400">
37
- <img class="h-3 w-3 fill-red-400" src="/img/close.svg" />
36
+ <h4 v-else class="text-red-400 flex justify-center">
37
+ <img width="12px" height="12px" class="fill-red-400" src="/img/close.svg" />
38
38
  </h4>
39
39
  </div>
40
40
 
41
41
  <div class="col-span-1 hidden lg:block">
42
- <h4 class="flex justify-center gap-1 text-white">
43
- <TagLabel v-for="tag in props.profile.tags" :key="tag" :text="tag" />
42
+ <h4 class="text-white flex justify-center gap-1">
43
+ <TagLabel v-for="tag in props.task.tags" :key="tag" :text="tag" />
44
44
  </h4>
45
45
  </div>
46
46
 
47
47
  <div class="col-span-1 flex">
48
- <ul class="profile-buttons">
48
+ <ul class="task-buttons bg-dark-600 px-2 rounded-full shadow-3xl py-1 items-center">
49
49
  <li>
50
50
  <button @click="edit">
51
51
  <EditIcon />
52
52
  </button>
53
53
  </li>
54
- <li v-if="props.profile.enabled">
54
+ <li v-if="props.task.enabled">
55
55
  <button @click="disable">
56
- <img class="h-4 w-4" src="/img/controls/disable.svg" />
56
+ <img width="16px" height="16px" class="mt-0.5" src="/img/controls/disable.svg" />
57
57
  </button>
58
58
  </li>
59
59
  <li v-else>
60
60
  <button @click="enable">
61
- <img class="h-4 w-4" src="/img/controls/enable.svg" />
61
+ <img width="16px" height="16px" class="mt-0.5" src="/img/controls/enable.svg" />
62
62
  </button>
63
63
  </li>
64
64
  <li>
@@ -76,111 +76,28 @@
76
76
  h4 {
77
77
  @apply text-center;
78
78
  }
79
- .profile-buttons {
80
- @apply mx-auto flex items-center justify-center rounded border border-dark-650 bg-dark-500;
81
- padding: 3px;
82
- gap: 2px;
83
-
84
- button {
85
- @apply relative flex items-center justify-center rounded border-0 outline-0 transition-all duration-150;
86
- background: transparent;
87
- width: 28px;
88
- height: 28px;
89
- color: #d0d0d3;
90
-
91
- &:hover {
92
- background: rgba(255, 255, 255, 0.1);
93
- color: #ffffff;
94
- transform: scale(1.05);
95
- }
96
-
97
- &:active {
98
- background: rgba(255, 255, 255, 0.2);
99
- transform: scale(0.95);
100
- }
101
- }
102
-
103
- svg,
104
- img {
105
- width: 16px;
106
- height: 16px;
107
- position: relative;
108
- z-index: 1;
109
- }
110
-
111
- svg path {
112
- fill: currentColor;
79
+ .task-buttons {
80
+ @apply flex mx-auto gap-x-3;
81
+ svg {
82
+ width: 15px;
83
+ height: 15px;
113
84
  }
114
85
  }
115
86
 
116
- // Tablet optimization
117
87
  @media (max-width: 1024px) {
118
88
  h4 {
119
89
  font-size: 10px !important;
120
90
  }
121
-
122
- .profile-buttons {
123
- padding: 3px;
124
- gap: 2px;
125
-
126
- button {
127
- width: 26px;
128
- height: 26px;
129
- }
130
-
131
- svg,
132
- img {
133
- width: 14px;
134
- height: 14px;
135
- }
91
+ .task-buttons {
92
+ @apply gap-x-3;
136
93
  }
137
-
138
- .profile-id {
94
+ .task-id {
139
95
  font-size: 6px !important;
140
96
  margin-right: -12px;
141
97
  margin-top: 20px;
142
98
  }
143
- }
144
-
145
- // Mobile optimization
146
- @media (max-width: 768px) {
147
- .profile-buttons {
148
- padding: 2px;
149
- gap: 1px;
150
-
151
- button {
152
- width: 22px;
153
- height: 22px;
154
- }
155
-
156
- svg,
157
- img {
158
- width: 12px;
159
- height: 12px;
160
- }
161
- }
162
- }
163
-
164
- // iPhone vertical (portrait) specific
165
- @media (max-width: 480px) and (orientation: portrait) {
166
- .profile-buttons {
167
- padding: 2px;
168
- gap: 1px;
169
-
170
- button {
171
- width: 18px;
172
- height: 18px;
173
-
174
- &:hover {
175
- transform: scale(1.1);
176
- }
177
- }
178
-
179
- svg,
180
- img {
181
- width: 10px;
182
- height: 10px;
183
- }
99
+ .task-id-alt {
100
+ font-size: 7px !important;
184
101
  }
185
102
  }
186
103
  </style>
@@ -190,30 +107,28 @@ import { PlayIcon, TrashIcon, BagWhiteIcon, PauseIcon, EditIcon } from "@/compon
190
107
  import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
191
108
  import { useUIStore } from "@/stores/ui";
192
109
  import { validateCard } from "@/stores/utils";
193
- import TagLabel from "@/components/Editors/TagLabel.vue";
110
+ import TagLabel from "@/components/editors/TagLabel.vue";
194
111
 
195
112
  const ui = useUIStore();
196
113
 
197
114
  const props = defineProps({
198
- profile: { type: Object }
115
+ task: { type: Object }
199
116
  });
200
117
 
201
118
  const getAccountType = () => {
202
- var cn = props.profile.cardNumber;
203
- if (cn.startsWith("4"))
204
- return `/img/banks/visa.svg`; // visa
205
- else if (cn.startsWith("3"))
206
- return `/img/banks/amex.svg`; // amex
119
+ var cn = props.task.cardNumber;
120
+ if (cn.startsWith("4")) return `/img/banks/visa.svg`; // visa
121
+ else if (cn.startsWith("3")) return `/img/banks/amex.svg`; // amex
207
122
  else if (cn.startsWith("5")) return `/img/banks/mastercard.svg`; // master
208
123
  };
209
124
 
210
125
  const expDate = () =>
211
- props.profile.privacy ? "••/••" : `${props.profile.expMonth}/${props.profile.expYear?.replace("20", "")}`;
212
- const enable = async () => await ui.addProfile({ ...props.profile, enabled: true });
213
- const disable = async () => await ui.addProfile({ ...props.profile, enabled: false });
126
+ props.task.privacy ? "••/••" : `${props.task.expMonth}/${props.task.expYear?.replace("20", "")}`;
127
+ const enable = async () => await ui.addProfile({ ...props.task, enabled: true });
128
+ const disable = async () => await ui.addProfile({ ...props.task, enabled: false });
214
129
  const edit = () => {
215
- ui.currentlyEditing = props.profile;
130
+ ui.currentlyEditing = props.task;
216
131
  ui.toggleModal("create-profile");
217
132
  };
218
- const deleteProfile = async () => await ui.deleteProfile(props.profile.id);
133
+ const deleteProfile = async () => await ui.deleteProfile(props.task._id);
219
134
  </script>
@@ -1,75 +1,43 @@
1
1
  <template>
2
2
  <div>
3
- <div class="dropdown input-default p-4 w-16 bg-dark-550 small-dropdown rounded-lg" ref="dropdownRef">
4
- <span @click="toggleOpen" class="flex justify-between items-center z-50 text-white">
3
+ <div class="dropdown input-default p-4 w-16 bg-dark-550 small-dropdown rounded-lg">
4
+ <span @click="open = !open" class="flex justify-between items-center z-inf text-white">
5
5
  <div class="flex gap-3 justify-center">
6
6
  <img class="w-5" :src="`/flags/${current?.toLowerCase()}.svg`" />
7
7
  </div>
8
8
  </span>
9
- <Teleport to="body">
9
+ <div
10
+ v-if="open && !disabled"
11
+ class="dropdown-content special-dropdown snap-mandatory snap-y z-inf max-h-48 overflow-scroll hidden-scrollbars"
12
+ >
10
13
  <div
11
- v-if="open && !disabled"
12
- class="dropdown-content-portal special-dropdown"
13
- :style="menuStyle"
14
- @click.stop
15
- @wheel.stop
16
- @touchmove.stop>
17
- <div
18
- v-for="(country, i) in countries"
19
- v-bind:key="country"
20
- :class="`cursor-pointer w-12 ${i === 0 ? '' : 'my-2'}`"
21
- @click="set(country)">
22
- <div class="flex justify-center items-center smooth-hover">
23
- <span class="text-sm">{{ country }}</span>
24
- <img class="w-5 ml-3" :src="`/flags/${country?.toLowerCase()}.svg`" />
25
- </div>
14
+ v-for="(country, i) in countries"
15
+ v-bind:key="country"
16
+ :class="`cursor-pointer w-12 snap-start ${i === 0 ? '' : 'my-2'}`"
17
+ @click="set(country)"
18
+ >
19
+ <div class="flex gap-3 justify-between smooth-hover">
20
+ <span class="text-sm">{{ country }} </span>
21
+ <img class="w-5" :src="`/flags/${country?.toLowerCase()}.svg`" />
26
22
  </div>
27
23
  </div>
28
- </Teleport>
24
+ </div>
29
25
  </div>
30
26
  </div>
31
27
  </template>
32
28
 
33
29
  <script setup>
34
30
  import { ref, watch } from "vue";
35
- import { useDropdownPosition } from "@/composables/useDropdownPosition";
36
- import { useClickOutside } from "@/composables/useClickOutside";
37
-
38
31
  const countries = ["US", "CA", "DE", "FR", "DK"];
39
32
  const open = ref(false);
40
- const dropdownRef = ref(null);
41
33
 
42
34
  const props = defineProps({
43
35
  value: { type: String },
44
36
  onClick: { type: Function },
45
37
  disabled: { type: Boolean, required: false }
46
38
  });
47
-
48
39
  const current = ref(props.value || "US");
49
40
 
50
- // Use composables for positioning and click outside
51
- const { menuStyle, updatePosition } = useDropdownPosition(dropdownRef, {
52
- offset: { x: -17.6, y: 4 },
53
- minWidth: 80,
54
- maxHeight: 192,
55
- zIndex: 99999999999999,
56
- estimateHeight: () => Math.min(countries.length * 32, 192)
57
- });
58
-
59
- useClickOutside(dropdownRef, () => {
60
- if (open.value) {
61
- open.value = false;
62
- }
63
- });
64
-
65
- const toggleOpen = () => {
66
- if (props.disabled) return;
67
- open.value = !open.value;
68
- if (open.value) {
69
- updatePosition();
70
- }
71
- };
72
-
73
41
  const set = (c) => {
74
42
  if (props.disabled) return;
75
43
  current.value = c;
@@ -89,49 +57,19 @@ watch(
89
57
  }
90
58
 
91
59
  .small-dropdown {
92
- @apply h-10 !important;
93
60
  background-clip: border-box !important;
94
61
  /* border-radius: 100% !important; */
95
62
  padding: 0;
96
63
  width: 3em !important;
64
+ /* height: 3em !important; */
97
65
  display: flex;
98
66
  justify-items: center;
99
67
  justify-content: center;
100
68
  }
101
69
 
102
- @media (min-width: 768px) {
103
- .small-dropdown {
104
- height: 40px !important; /* Match input-default responsive height */
105
- }
106
- }
107
-
108
- .dropdown-content-portal {
109
- @apply bg-dark-400 border border-dark-650 rounded-lg shadow-2xl z-50;
110
- padding: 0.5rem;
111
- max-height: 192px !important;
112
- overflow-y: auto !important;
113
- overscroll-behavior: contain !important;
114
- touch-action: pan-y !important;
115
- -webkit-overflow-scrolling: touch !important;
116
- scrollbar-width: none;
117
- -ms-overflow-style: none;
118
- }
119
-
120
- .dropdown-content-portal::-webkit-scrollbar {
121
- display: none;
122
- }
123
-
124
- .dropdown-content-portal > div {
125
- @apply px-3 py-2 text-sm text-white cursor-pointer;
126
- border-radius: 6px;
127
- }
128
-
129
- .dropdown-content-portal > div:hover {
130
- /* Removed hover background */
131
- }
132
-
133
- .dropdown-content-portal > div .flex {
134
- @apply items-center justify-center;
135
- gap: 0.75rem;
70
+ .dropdown-content {
71
+ left: -1.1rem;
72
+ z-index: 99999999999999;
136
73
  }
137
74
  </style>
75
+ , watch
@@ -1,64 +1,64 @@
1
1
  <template>
2
2
  <Table>
3
- <Header class="grid-cols-7 text-center lg:grid-cols-8">
4
- <div class="col-span-3 flex lg:col-span-2">
3
+ <Header class="text-center grid-cols-7 lg:grid-cols-8">
4
+ <div class="col-span-3 lg:col-span-2 flex">
5
5
  <Checkbox
6
6
  class="mr-3"
7
7
  :toggled="ui.mainCheckbox.profiles"
8
8
  @valueUpdate="ui.toggleMainCheckbox('profiles')"
9
- :isHeader="true" />
9
+ />
10
10
  <div class="mx-auto flex items-center" @click="ui.toggleSort('eventId')">
11
- <ProfileIcon class="mr-0 h-4 w-4 md:mr-3" />
12
- <h4 class="hidden md:flex">Profile Name</h4>
11
+ <img src="/img/profile.svg" width="14" class="mr-0 ipadlg:mr-3" />
12
+ <h4 class="hidden ipadlg:flex">Profile Name</h4>
13
+ <!-- <DownIcon v-if="ui.sortData.sortBy === 'eventId'" class="ml-1" /> -->
13
14
  </div>
14
15
  </div>
15
- <div class="col-span-1 flex items-center justify-center lg:col-span-2" v-once>
16
- <CartIcon class="mr-0 h-4 w-4 md:mr-3" />
17
- <h4 class="hidden md:flex">Card Number</h4>
16
+ <div class="lg:col-span-2 col-span-1 items-center justify-center flex" v-once>
17
+ <CartIcon class="mr-0 ipadlg:mr-3" />
18
+ <h4 class="hidden ipadlg:flex">Card Number</h4>
18
19
  </div>
19
20
  <div class="col-span-1 flex items-center justify-center" v-once>
20
- <TimerIcon class="mr-0 h-4 w-4 md:mr-3" />
21
- <h4 class="hidden md:flex">Expiration</h4>
21
+ <TimerIcon class="mr-0 ipadlg:mr-3" />
22
+ <h4 class="hidden ipadlg:flex">Exp. Date</h4>
22
23
  </div>
23
24
  <div class="col-span-1 flex items-center justify-center" v-once>
24
- <CheckmarkIcon class="mr-0 h-4 w-4 md:mr-3" />
25
- <h4 class="hidden md:flex">Enabled</h4>
25
+ <img src="/img/controls/enable.svg" width="14" class="mr-0 ipadlg:mr-3" />
26
+ <h4 class="hidden ipadlg:flex">Enabled</h4>
26
27
  </div>
27
- <div class="col-span-1 hidden items-center justify-center lg:flex" v-once>
28
- <TicketIcon class="mr-0 h-4 w-4 lg:mr-3" />
28
+ <div class="col-span-1 hidden lg:flex items-center justify-center" v-once>
29
+ <TicketIcon class="mr-0 lg:mr-3" />
29
30
  <h4 class="hidden lg:flex">Tags</h4>
30
31
  </div>
31
32
  <div class="col-span-1 flex items-center justify-center" v-once>
32
- <ClickIcon class="mr-0 h-4 w-4 md:mr-3" />
33
- <h4 class="hidden md:flex">Actions</h4>
33
+ <ClickIcon class="mr-0 ipadlg:mr-3" />
34
+ <h4 class="hidden ipadlg:flex">Actions</h4>
34
35
  </div>
35
36
  </Header>
36
- <div v-if="toRender.length != 0">
37
+ <div v-if="toRender.length > 0">
37
38
  <RecycleScroller
38
39
  :items="toRender"
39
40
  :item-size="64"
40
41
  key-field="index"
41
- class="scroller vue-recycle-scroller ready direction-vertical hidden-scrollbars stop-pan flex flex-col divide-y divide-dark-650 overflow-y-auto overflow-x-hidden"
42
- :style="{ maxHeight: dynamicTableHeight }">
43
- <template #default="props">
44
- <div class="profile" :key="`profile-${props.item.id || props.item.index}`">
42
+ class="scroller max-h-big vue-recycle-scroller ready direction-vertical flex flex-col divide-y-2 divide-border overflow-y-auto hidden-scrollbars overflow-x-hidden stop-pan"
43
+ >
44
+ <template #default="props" :key="i[props.item.idex]">
45
+ <div class="task">
45
46
  <Profile
46
47
  @click="i[props.item.index]++"
47
- :class="props.item.index % 2 == 1 ? 'table-row-even' : 'table-row-odd'"
48
- :profile="props.item" />
48
+ :class="[props.item.index % 2 == 1 ? 'bg-dark-500' : 'bg-dark-550']"
49
+ :task="props.item"
50
+ />
49
51
  </div>
50
52
  </template>
51
53
  </RecycleScroller>
52
54
  </div>
53
- <div v-else class="empty-state flex flex-col items-center justify-center bg-dark-400 py-8 text-center">
54
- <ProfileIcon class="mb-3 h-12 w-12 text-dark-400 opacity-50" />
55
- <p class="text-sm text-light-400">No profiles found</p>
56
- <p class="mt-1 text-xs text-light-500">Create profiles to get started</p>
55
+ <div v-else class="flex justify-center text-light-400 py-2 bg-dark-500 border-b-2 border-border">
56
+ No profiles found
57
57
  </div>
58
58
  </Table>
59
59
  </template>
60
60
  <style lang="scss" scoped>
61
- .profile {
61
+ .task {
62
62
  height: 64px;
63
63
  }
64
64
  h4 {
@@ -73,82 +73,25 @@ h4 {
73
73
  max-height: calc(100vh - 20rem);
74
74
  overflow: hidden;
75
75
  }
76
-
77
- .empty-state {
78
- color: #969696;
79
- font-size: 14px;
80
- font-weight: 500;
81
- }
82
76
  </style>
83
77
  <script setup>
84
78
  import { Table, Header } from "@/components/Table";
85
- import { CartIcon, TicketIcon, ClickIcon, TimerIcon, ProfileIcon, CheckmarkIcon } from "@/components/icons";
79
+ import { CartIcon, TicketIcon, ClickIcon, TimerIcon } from "@/components/icons";
86
80
  import Profile from "./Profile.vue";
87
81
  import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
88
82
  import { useUIStore } from "@/stores/ui";
89
- import { computed, ref, onMounted, onUnmounted } from "vue";
83
+ import { computed, ref } from "vue";
90
84
 
91
85
  const props = defineProps({
92
- profiles: { type: Object }
86
+ tasks: { type: Object }
93
87
  });
88
+ const i = ref({});
89
+ props.tasks.forEach((t) => (i.value[t._id] = 0));
94
90
 
95
91
  const ui = useUIStore();
96
92
 
97
- const i = ref({});
98
93
  const toRender = computed(() => {
99
94
  let c = 0;
100
- const rendered = props.profiles.map((t) => ({ ...t, index: c++ }));
101
-
102
- // Initialize reactive refs for click tracking
103
- rendered.forEach((t) => {
104
- if (t.id && !(t.id in i.value)) {
105
- i.value[t.id] = 0;
106
- }
107
- if (!(t.index in i.value)) {
108
- i.value[t.index] = 0;
109
- }
110
- });
111
-
112
- return rendered;
113
- });
114
-
115
- // Dynamic height calculation for perfect item fitting
116
- const windowHeight = ref(window.innerHeight);
117
- const windowWidth = ref(window.innerWidth);
118
-
119
- const updateDimensions = () => {
120
- windowHeight.value = window.innerHeight;
121
- windowWidth.value = window.innerWidth;
122
- };
123
-
124
- onMounted(() => {
125
- window.addEventListener("resize", updateDimensions);
126
- });
127
-
128
- onUnmounted(() => {
129
- window.removeEventListener("resize", updateDimensions);
130
- });
131
-
132
- const dynamicTableHeight = computed(() => {
133
- // Calculate available space for profiles table with conservative buffer
134
- const headerHeight = 60; // Header + navbar
135
- const titleHeight = 50; // Profiles title and controls
136
- const searchHeight = 50; // Search and filter controls
137
- const margins = windowWidth.value >= 1024 ? 40 : 25;
138
- const bufferSpace = 50; // Conservative buffer to prevent partial items
139
-
140
- const totalUsedSpace = headerHeight + titleHeight + searchHeight + margins + bufferSpace;
141
- const availableHeight = windowHeight.value - totalUsedSpace;
142
-
143
- // Profile row height is always 64px
144
- const rowHeight = 64;
145
- const minRowsToShow = 2;
146
- const minHeight = minRowsToShow * rowHeight;
147
-
148
- // Calculate exact number of complete rows that fit with conservative approach
149
- const maxCompleteRows = Math.floor(Math.max(availableHeight, minHeight) / rowHeight);
150
- const exactHeight = maxCompleteRows * rowHeight;
151
-
152
- return exactHeight + "px";
95
+ return props.tasks.map((t) => ({ ...t, index: c++ }));
153
96
  });
154
97
  </script>
@@ -1,77 +1,16 @@
1
1
  <template>
2
- <div class="tag-pill">
3
- <span class="tag-text">{{ formatText(props.text) }}</span>
2
+ <div class="flex rounded-2xl w-fit shadow-3xl items-center justify-center bg-dark-600">
3
+ <span class="font-bold p-2 truncate small">{{ props.text }}</span>
4
4
  </div>
5
5
  </template>
6
6
 
7
- <style lang="scss" scoped>
8
- .tag-pill {
9
- @apply inline-flex items-center justify-center rounded-md transition-all duration-200;
10
- background: linear-gradient(145deg, #3d3e44, #35363c);
11
- border: 1px solid #4b4c53;
12
- padding: 0.1875rem 0.5rem;
13
- min-width: 2rem;
14
- max-width: 4.5rem;
15
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.05);
16
-
17
- &:hover {
18
- background: linear-gradient(145deg, #44454b, #3d3e44);
19
- border-color: #52535a;
20
- transform: translateY(-0.5px);
21
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.08);
22
- }
23
- }
24
-
25
- .tag-text {
26
- @apply text-white font-semibold truncate;
27
- font-size: 0.6875rem;
28
- line-height: 1.1;
29
- letter-spacing: 0.025em;
30
- text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
31
- }
32
-
33
- // Ultra responsive design
34
- @media (max-width: 1024px) {
35
- .tag-pill {
36
- padding: 0.1rem 0.3rem;
37
- max-width: 3.5rem;
38
- }
39
-
40
- .tag-text {
41
- font-size: 0.575rem;
42
- }
43
- }
44
-
45
- @media (max-width: 768px) {
46
- .tag-pill {
47
- padding: 0.075rem 0.25rem;
48
- max-width: 3rem;
49
- min-width: 1.25rem;
50
- }
51
-
52
- .tag-text {
53
- font-size: 0.55rem;
54
- }
55
- }
56
-
57
- @media (max-width: 480px) {
58
- .tag-pill {
59
- padding: 0.05rem 0.2rem;
60
- max-width: 2.5rem;
61
- min-width: 1rem;
62
- }
63
-
64
- .tag-text {
65
- font-size: 0.5rem;
66
- }
7
+ <style scoped>
8
+ .small {
9
+ font-size: 0.6rem;
10
+ line-height: 0.8rem;
67
11
  }
68
12
  </style>
69
13
 
70
14
  <script setup>
71
15
  const props = defineProps({ text: { type: String } });
72
-
73
- const formatText = (text) => {
74
- if (!text) return "";
75
- return text.toUpperCase();
76
- };
77
16
  </script>