@necrolab/dashboard 0.4.3

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 (240) hide show
  1. package/.claude/settings.local.json +45 -0
  2. package/.eslintrc.js +24 -0
  3. package/.prettierignore +1 -0
  4. package/.prettierrc +10 -0
  5. package/.vscode/extensions.json +3 -0
  6. package/ICONS.md +21 -0
  7. package/README.md +65 -0
  8. package/backend/api.js +430 -0
  9. package/backend/auth.js +62 -0
  10. package/backend/batching.js +43 -0
  11. package/backend/endpoints.js +343 -0
  12. package/backend/index.js +23 -0
  13. package/backend/mock-data.js +66 -0
  14. package/backend/mock-src/classes/logger.js +112 -0
  15. package/backend/mock-src/classes/utils.js +42 -0
  16. package/backend/mock-src/ticketmaster.js +92 -0
  17. package/backend/validator.js +62 -0
  18. package/config/configs.json +20 -0
  19. package/config/filter.json +3 -0
  20. package/config/presale.csv +3 -0
  21. package/config/proxies.txt +6 -0
  22. package/config/used-codes.json +4 -0
  23. package/index.html +114 -0
  24. package/index.js +2 -0
  25. package/jsconfig.json +16 -0
  26. package/package.json +48 -0
  27. package/postcss.config.js +6 -0
  28. package/postinstall.js +9 -0
  29. package/public/android-chrome-192x192.png +0 -0
  30. package/public/android-chrome-512x512.png +0 -0
  31. package/public/apple-touch-icon.png +0 -0
  32. package/public/favicon-16x16.png +0 -0
  33. package/public/favicon-32x32.png +0 -0
  34. package/public/favicon.ico +0 -0
  35. package/public/flags/ae.svg +1 -0
  36. package/public/flags/at.svg +1 -0
  37. package/public/flags/au.svg +1 -0
  38. package/public/flags/be.svg +1 -0
  39. package/public/flags/ch.svg +1 -0
  40. package/public/flags/cz.svg +1 -0
  41. package/public/flags/de.svg +1 -0
  42. package/public/flags/dk.svg +1 -0
  43. package/public/flags/es.svg +1 -0
  44. package/public/flags/nl.svg +1 -0
  45. package/public/flags/no.svg +1 -0
  46. package/public/flags/nz.svg +1 -0
  47. package/public/flags/pl.svg +1 -0
  48. package/public/flags/se.svg +1 -0
  49. package/public/flags/uk.svg +1 -0
  50. package/public/flags/us.svg +1 -0
  51. package/public/img/award.svg +3 -0
  52. package/public/img/background.svg +14 -0
  53. package/public/img/bag_w.svg +12 -0
  54. package/public/img/banks/amex.svg +4 -0
  55. package/public/img/banks/mastercard.svg +4 -0
  56. package/public/img/banks/visa.svg +4 -0
  57. package/public/img/camera.svg +3 -0
  58. package/public/img/close.svg +3 -0
  59. package/public/img/controls/disable.svg +5 -0
  60. package/public/img/controls/enable.svg +5 -0
  61. package/public/img/groups.svg +3 -0
  62. package/public/img/hand.svg +3 -0
  63. package/public/img/key.svg +3 -0
  64. package/public/img/logo.png +0 -0
  65. package/public/img/logo_icon.png +0 -0
  66. package/public/img/logo_icon_2.png +0 -0
  67. package/public/img/logo_trans.png +0 -0
  68. package/public/img/loyalty.svg +3 -0
  69. package/public/img/mail.svg +3 -0
  70. package/public/img/pencil.svg +3 -0
  71. package/public/img/profile.svg +4 -0
  72. package/public/img/reload.svg +3 -0
  73. package/public/img/sandclock.svg +25 -0
  74. package/public/img/save.svg +5 -0
  75. package/public/img/savings.svg +3 -0
  76. package/public/img/scanner.svg +3 -0
  77. package/public/img/sell.svg +3 -0
  78. package/public/img/shield.svg +3 -0
  79. package/public/img/ski.svg +3 -0
  80. package/public/img/stadium.svg +8 -0
  81. package/public/img/stadium_w.svg +8 -0
  82. package/public/img/timer.svg +3 -0
  83. package/public/manifest.json +27 -0
  84. package/public/robots.txt +2 -0
  85. package/run +10 -0
  86. package/src/App.vue +307 -0
  87. package/src/assets/css/_input.scss +197 -0
  88. package/src/assets/css/main.scss +269 -0
  89. package/src/assets/css/tailwind.css +3 -0
  90. package/src/assets/img/award.svg +3 -0
  91. package/src/assets/img/background.svg +11 -0
  92. package/src/assets/img/camera.svg +3 -0
  93. package/src/assets/img/close.svg +3 -0
  94. package/src/assets/img/eyes/closed.svg +13 -0
  95. package/src/assets/img/eyes/open.svg +12 -0
  96. package/src/assets/img/groups.svg +3 -0
  97. package/src/assets/img/hand.svg +3 -0
  98. package/src/assets/img/key.svg +3 -0
  99. package/src/assets/img/logo.png +0 -0
  100. package/src/assets/img/logo_icon.png +0 -0
  101. package/src/assets/img/logo_icon_2.png +0 -0
  102. package/src/assets/img/logo_trans.png +0 -0
  103. package/src/assets/img/loyalty.svg +3 -0
  104. package/src/assets/img/mail.svg +3 -0
  105. package/src/assets/img/pencil.svg +3 -0
  106. package/src/assets/img/reload.svg +3 -0
  107. package/src/assets/img/savings.svg +3 -0
  108. package/src/assets/img/scanner.svg +3 -0
  109. package/src/assets/img/sell.svg +3 -0
  110. package/src/assets/img/shield.svg +3 -0
  111. package/src/assets/img/ski.svg +3 -0
  112. package/src/assets/img/square_check.svg +5 -0
  113. package/src/assets/img/square_uncheck.svg +5 -0
  114. package/src/assets/img/stadium.svg +8 -0
  115. package/src/assets/img/timer.svg +3 -0
  116. package/src/assets/img/wildcard.svg +7 -0
  117. package/src/components/Auth/LoginForm.vue +48 -0
  118. package/src/components/Editors/Account/Account.vue +119 -0
  119. package/src/components/Editors/Account/AccountCreator.vue +147 -0
  120. package/src/components/Editors/Account/AccountView.vue +87 -0
  121. package/src/components/Editors/Account/CreateAccount.vue +106 -0
  122. package/src/components/Editors/Profile/CreateProfile.vue +321 -0
  123. package/src/components/Editors/Profile/Profile.vue +142 -0
  124. package/src/components/Editors/Profile/ProfileCountryChooser.vue +75 -0
  125. package/src/components/Editors/Profile/ProfileView.vue +96 -0
  126. package/src/components/Editors/TagLabel.vue +16 -0
  127. package/src/components/Editors/TagToggle.vue +41 -0
  128. package/src/components/Filter/Filter.vue +409 -0
  129. package/src/components/Filter/FilterPreview.vue +236 -0
  130. package/src/components/Filter/PriceSortToggle.vue +105 -0
  131. package/src/components/Table/Header.vue +5 -0
  132. package/src/components/Table/Row.vue +5 -0
  133. package/src/components/Table/Table.vue +14 -0
  134. package/src/components/Table/index.js +4 -0
  135. package/src/components/Tasks/CheckStock.vue +62 -0
  136. package/src/components/Tasks/Controls/DesktopControls.vue +73 -0
  137. package/src/components/Tasks/Controls/MobileControls.vue +32 -0
  138. package/src/components/Tasks/Controls/index.js +3 -0
  139. package/src/components/Tasks/CreateTaskAXS.vue +339 -0
  140. package/src/components/Tasks/CreateTaskTM.vue +459 -0
  141. package/src/components/Tasks/MassEdit.vue +50 -0
  142. package/src/components/Tasks/QuickSettings.vue +167 -0
  143. package/src/components/Tasks/ScrapeVenue.vue +42 -0
  144. package/src/components/Tasks/Stats.vue +66 -0
  145. package/src/components/Tasks/Task.vue +296 -0
  146. package/src/components/Tasks/TaskLabel.vue +20 -0
  147. package/src/components/Tasks/TaskView.vue +126 -0
  148. package/src/components/Tasks/Utilities.vue +33 -0
  149. package/src/components/icons/Award.vue +8 -0
  150. package/src/components/icons/Bag.vue +8 -0
  151. package/src/components/icons/BagWhite.vue +8 -0
  152. package/src/components/icons/Box.vue +8 -0
  153. package/src/components/icons/Camera.vue +8 -0
  154. package/src/components/icons/Cart.vue +8 -0
  155. package/src/components/icons/Check.vue +5 -0
  156. package/src/components/icons/Checkmark.vue +11 -0
  157. package/src/components/icons/Click.vue +8 -0
  158. package/src/components/icons/Close.vue +21 -0
  159. package/src/components/icons/CloseX.vue +5 -0
  160. package/src/components/icons/Console.vue +13 -0
  161. package/src/components/icons/Down.vue +8 -0
  162. package/src/components/icons/Edit.vue +13 -0
  163. package/src/components/icons/Event.vue +8 -0
  164. package/src/components/icons/Expand.vue +8 -0
  165. package/src/components/icons/Filter.vue +13 -0
  166. package/src/components/icons/Gear.vue +8 -0
  167. package/src/components/icons/Group.vue +8 -0
  168. package/src/components/icons/Hand.vue +8 -0
  169. package/src/components/icons/Key.vue +21 -0
  170. package/src/components/icons/Logout.vue +13 -0
  171. package/src/components/icons/Loyalty.vue +8 -0
  172. package/src/components/icons/Mail.vue +8 -0
  173. package/src/components/icons/Menu.vue +8 -0
  174. package/src/components/icons/Pause.vue +5 -0
  175. package/src/components/icons/Pencil.vue +21 -0
  176. package/src/components/icons/Play.vue +8 -0
  177. package/src/components/icons/Plus.vue +8 -0
  178. package/src/components/icons/Profile.vue +18 -0
  179. package/src/components/icons/Reload.vue +7 -0
  180. package/src/components/icons/Sandclock.vue +33 -0
  181. package/src/components/icons/Savings.vue +8 -0
  182. package/src/components/icons/Scanner.vue +8 -0
  183. package/src/components/icons/Scrape.vue +8 -0
  184. package/src/components/icons/Sell.vue +21 -0
  185. package/src/components/icons/Shield.vue +8 -0
  186. package/src/components/icons/Shrink.vue +8 -0
  187. package/src/components/icons/Ski.vue +8 -0
  188. package/src/components/icons/Spinner.vue +42 -0
  189. package/src/components/icons/SquareCheck.vue +18 -0
  190. package/src/components/icons/SquareUncheck.vue +18 -0
  191. package/src/components/icons/Stadium.vue +13 -0
  192. package/src/components/icons/StadiumWhite.vue +13 -0
  193. package/src/components/icons/Status.vue +8 -0
  194. package/src/components/icons/Tag.vue +8 -0
  195. package/src/components/icons/Tasks.vue +13 -0
  196. package/src/components/icons/Ticket.vue +8 -0
  197. package/src/components/icons/Timer.vue +8 -0
  198. package/src/components/icons/Trash.vue +8 -0
  199. package/src/components/icons/Up.vue +10 -0
  200. package/src/components/icons/Wildcard.vue +18 -0
  201. package/src/components/icons/index.js +111 -0
  202. package/src/components/ui/Modal.vue +61 -0
  203. package/src/components/ui/Navbar.vue +207 -0
  204. package/src/components/ui/ReconnectIndicator.vue +90 -0
  205. package/src/components/ui/Splash.vue +24 -0
  206. package/src/components/ui/controls/CountryChooser.vue +87 -0
  207. package/src/components/ui/controls/EyeToggle.vue +11 -0
  208. package/src/components/ui/controls/atomic/Checkbox.vue +28 -0
  209. package/src/components/ui/controls/atomic/Dropdown.vue +138 -0
  210. package/src/components/ui/controls/atomic/LoadingButton.vue +45 -0
  211. package/src/components/ui/controls/atomic/MultiDropdown.vue +262 -0
  212. package/src/components/ui/controls/atomic/Switch.vue +84 -0
  213. package/src/libs/Filter.js +593 -0
  214. package/src/libs/ansii.js +565 -0
  215. package/src/libs/panzoom.js +1413 -0
  216. package/src/main.js +23 -0
  217. package/src/registerServiceWorker.js +32 -0
  218. package/src/router/index.js +65 -0
  219. package/src/stores/cities.json +1 -0
  220. package/src/stores/connection.js +399 -0
  221. package/src/stores/countries.js +88 -0
  222. package/src/stores/logger.js +103 -0
  223. package/src/stores/requests.js +88 -0
  224. package/src/stores/sampleData.js +1034 -0
  225. package/src/stores/ui.js +584 -0
  226. package/src/stores/utils.js +554 -0
  227. package/src/types/index.js +42 -0
  228. package/src/utils/debug.js +1 -0
  229. package/src/views/Accounts.vue +191 -0
  230. package/src/views/Console.vue +224 -0
  231. package/src/views/Editor.vue +785 -0
  232. package/src/views/FilterBuilder.vue +785 -0
  233. package/src/views/Login.vue +27 -0
  234. package/src/views/Profiles.vue +209 -0
  235. package/src/views/Tasks.vue +157 -0
  236. package/static/offline.html +184 -0
  237. package/tailwind.config.js +57 -0
  238. package/vite.config.js +63 -0
  239. package/vue.config.js +32 -0
  240. package/workbox-config.js +66 -0
@@ -0,0 +1,321 @@
1
+ <template>
2
+ <Modal>
3
+ <template #header>
4
+ <span v-if="!ui.currentlyEditing?.profileName">Add Profile</span>
5
+ <span v-else>Edit Profile</span>
6
+ <img src="@/assets/img/scanner.svg" class="ml-4" />
7
+ </template>
8
+
9
+ <div>
10
+ <div class="grid grid-cols-12 gap-3 my-3">
11
+ <!-- Profile tag -->
12
+ <div class="input-wrapper col-span-4" style="z-index: 30 !important">
13
+ <label class="label-override mb-2">Profile Tag </label>
14
+ <Dropdown
15
+ :class="`input-default dropdown w-full ${errors.includes('profileTag') ? 'error' : ''}`"
16
+ :default="ui.profile.accountTags[0]"
17
+ :options="ui.profile.accountTags"
18
+ :onClick="(f) => (profile.tag = f)"
19
+ :capitalize="true"
20
+ />
21
+ </div>
22
+
23
+ <!-- Card Number -->
24
+ <div class="input-wrapper col-span-8 z-0">
25
+ <label class="label-override mb-2">Card Number </label>
26
+ <div :class="`input-default ${errors.includes('cardNumber') ? 'error' : ''}`">
27
+ <input
28
+ ref="cardNumberInput"
29
+ placeholder="Enter card number"
30
+ v-model="displayCardNumber"
31
+ maxlength="23"
32
+ inputmode="numeric"
33
+ @input="handleCreditCardUpdate"
34
+ @focus="formatCardNumberDisplay"
35
+ />
36
+ </div>
37
+ </div>
38
+
39
+ <!-- Country chooser -->
40
+ <div class="input-wrapper col-span-2">
41
+ <label class="label-override mb-2">Country </label>
42
+ <ProfileCountryChooser
43
+ class="h-8"
44
+ :value="profile.country"
45
+ :onClick="chooseCountry"
46
+ :disabled="true"
47
+ />
48
+ </div>
49
+
50
+ <!-- Exp Year -->
51
+ <div class="input-wrapper col-span-5">
52
+ <label class="label-override mb-2 z-0">Expiry Year </label>
53
+ <Dropdown
54
+ :class="`input-default dropdown w-full ${errors.includes('expYear') ? 'error' : ''}`"
55
+ default="Expiry Year"
56
+ :value="
57
+ profile.expYear && !profile?.expYear?.startsWith('20')
58
+ ? '20' + profile.expYear
59
+ : profile.expYear
60
+ ? profile.expYear
61
+ : undefined
62
+ "
63
+ :options="['2025', '2026', '2027', '2028', '2029', '2030', '2031']"
64
+ :onClick="(f) => (profile.expYear = f)"
65
+ />
66
+ </div>
67
+
68
+ <!-- Exp Month -->
69
+ <div class="input-wrapper col-span-5">
70
+ <label class="label-override mb-2">Expiry Month </label>
71
+ <Dropdown
72
+ :class="`input-default dropdown w-full ${errors.includes('expMonth') ? 'error' : ''}`"
73
+ default="Expiry Month"
74
+ :value="profile.expMonth ? profile.expMonth : undefined"
75
+ :options="['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']"
76
+ :onClick="(f) => (profile.expMonth = f)"
77
+ />
78
+ </div>
79
+
80
+ <!-- CVV -->
81
+ <div class="input-wrapper col-span-6 md:col-span-4 z-0">
82
+ <label class="label-override mb-2">CVV </label>
83
+ <div :class="`input-default ${errors.includes('cvv') ? 'error' : ''}`">
84
+ <input
85
+ placeholder="183"
86
+ min="100"
87
+ max="9999"
88
+ maxlength="4"
89
+ minlength="3"
90
+ v-model="profile.cvv"
91
+ />
92
+ </div>
93
+ </div>
94
+
95
+ <!-- City -->
96
+ <div class="input-wrapper col-span-6 md:col-span-4 z-0">
97
+ <label class="label-override mb-2">City </label>
98
+ <div :class="`input-default ${errors.includes('city') ? 'error' : ''}`">
99
+ <input placeholder="Denver" v-model="profile.city" />
100
+ </div>
101
+ </div>
102
+
103
+ <!-- State -->
104
+ <div class="input-wrapper col-span-6 md:col-span-4 z-50">
105
+ <label class="label-override mb-2">State </label>
106
+ <div v-if="profile.country === 'US'" :class="`${errors.includes('state') ? 'error' : ''}`">
107
+ <Dropdown
108
+ class="input-default w-full"
109
+ default="Select State"
110
+ :onClick="(state) => profile.state = state"
111
+ :options="usStates"
112
+ :allowDefault="false"
113
+ rightAmount="right-2"
114
+ :chosen="profile.state"
115
+ />
116
+ </div>
117
+ <div v-else :class="`input-default ${errors.includes('state') ? 'error' : ''}`">
118
+ <input disabled placeholder="N/A" value="" />
119
+ </div>
120
+ </div>
121
+
122
+ <!-- Zip -->
123
+ <div class="input-wrapper col-span-6 md:col-span-4 z-0">
124
+ <label class="label-override mb-2">Zip </label>
125
+ <div :class="`input-default ${errors.includes('zipCode') ? 'error' : ''}`">
126
+ <input placeholder="80281" type="number" min="1" max="12" v-model="profile.zipCode" />
127
+ </div>
128
+ </div>
129
+
130
+ <!-- Address -->
131
+ <div class="input-wrapper col-span-6 md:col-span-4 z-0">
132
+ <label class="label-override mb-2">Address </label>
133
+ <div :class="`input-default ${errors.includes('address') ? 'error' : ''}`">
134
+ <input placeholder="Denver" v-model="profile.address" />
135
+ </div>
136
+ </div>
137
+
138
+ <!-- Generate -->
139
+ <div class="input-wrapper col-span-6 md:col-span-4 z-0">
140
+ <label class="label-override mb-2 z-0"> Fake ID</label>
141
+ <div class="input-default mt-2 w-full h-10 flex items-center">
142
+ <button @click="generate" class="text-white w-full text-xs flex items-center justify-center gap-2">
143
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
144
+ <rect x="3" y="4" width="18" height="16" rx="2" />
145
+ <line x1="7" y1="2" x2="7" y2="6" />
146
+ <line x1="17" y1="2" x2="17" y2="6" />
147
+ </svg>
148
+ <span>Generate</span>
149
+ </button>
150
+ </div>
151
+ </div>
152
+ </div>
153
+ </div>
154
+
155
+ <button
156
+ 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-auto mt-4"
157
+ @click="done()"
158
+ >
159
+ Save <EditIcon />
160
+ </button>
161
+ </Modal>
162
+ </template>
163
+ <style lang="scss" scoped>
164
+ .input-wrapper {
165
+ label {
166
+ @apply flex;
167
+ }
168
+ }
169
+ .z-0 {
170
+ z-index: 0 !important;
171
+ }
172
+
173
+ .error {
174
+ border-width: 2px !important;
175
+ border-color: rgb(238 130 130) !important;
176
+ }
177
+ </style>
178
+ <script setup>
179
+ import Modal from "@/components/ui/Modal.vue";
180
+ import { EditIcon } from "@/components/icons";
181
+ import ProfileCountryChooser from "@/components/Editors/Profile/ProfileCountryChooser.vue";
182
+ import { useUIStore } from "@/stores/ui";
183
+ import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
184
+ import { ref, computed, watch, nextTick, onMounted } from "vue";
185
+ import { fakeId } from "@/stores/utils";
186
+ import { validateCard } from "@/stores/utils";
187
+
188
+ const props = defineProps({ profile: { type: Object, required: false } });
189
+ const errors = ref([]);
190
+ const ui = useUIStore();
191
+
192
+ // US States list (excluding GA and PR, alphabetically ordered)
193
+ const usStates = [
194
+ "AK", "AL", "AR", "AZ", "CA", "CO", "CT", "DE", "FL", "HI", "IA", "ID", "IL", "IN", "KS", "KY", "LA", "MA", "MD", "ME", "MI", "MN", "MO", "MS", "MT", "NC", "ND", "NE", "NH", "NJ", "NM", "NV", "NY", "OH", "OK", "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VA", "VT", "WA", "WI", "WV", "WY"
195
+ ];
196
+ const cardNumberInput = ref(null);
197
+ const profile = ref({
198
+ cvv: "",
199
+ cardNumber: "",
200
+ city: "",
201
+ tag: ui.profile.accountTags[0],
202
+ state: "",
203
+ country: "US",
204
+ zipCode: ""
205
+ });
206
+
207
+ if (ui.currentlyEditing?.profileName) profile.value = ui.currentlyEditing;
208
+
209
+ // Reactive display value for the formatted card number
210
+ const displayCardNumber = ref("");
211
+
212
+ // Initialize display card number with formatting
213
+ const initializeCardNumberDisplay = () => {
214
+ if (profile.value.cardNumber) {
215
+ const cleanNumber = profile.value.cardNumber.replace(/\s+/g, "");
216
+ if (cleanNumber) {
217
+ const cardInfo = validateCard(cleanNumber);
218
+ displayCardNumber.value = cardInfo.formatted;
219
+ }
220
+ }
221
+ };
222
+
223
+ // Format card number display on focus
224
+ const formatCardNumberDisplay = () => {
225
+ if (profile.value.cardNumber) {
226
+ const cleanNumber = profile.value.cardNumber.replace(/\s+/g, "");
227
+ if (cleanNumber) {
228
+ const cardInfo = validateCard(cleanNumber);
229
+ displayCardNumber.value = cardInfo.formatted;
230
+ }
231
+ }
232
+ };
233
+
234
+ // Watch for changes in profile.cardNumber to sync with display
235
+ watch(() => profile.value.cardNumber, (newValue) => {
236
+ if (newValue) {
237
+ const cleanNumber = newValue.replace(/\s+/g, "");
238
+ if (cleanNumber) {
239
+ const cardInfo = validateCard(cleanNumber);
240
+ displayCardNumber.value = cardInfo.formatted;
241
+ }
242
+ } else {
243
+ displayCardNumber.value = "";
244
+ }
245
+ });
246
+
247
+ // Initialize on modal open
248
+ onMounted(() => {
249
+ nextTick(() => {
250
+ initializeCardNumberDisplay();
251
+ });
252
+ });
253
+
254
+ const generate = () => {
255
+ const fake = fakeId();
256
+ profile.value.city = fake.city;
257
+ profile.value.zipCode = fake?.zipCode;
258
+ profile.value.address = fake.address;
259
+ profile.value.state = fake.state;
260
+ profile.value.country = "US";
261
+ };
262
+
263
+ const chooseCountry = (f) => {
264
+ profile.value.country = f;
265
+ if (f !== "US") profile.value.state = "";
266
+ };
267
+
268
+ const validate = (p) => {
269
+ errors.value = [];
270
+ if (!p.zipCode && !p.address && !p.state && !p.city) generate();
271
+
272
+ if (!p.zipCode) errors.value.push("zipCode");
273
+ if (!p.address) errors.value.push("address");
274
+ if (!p.state && p.country === "US") errors.value.push("state");
275
+ if (!p.city) errors.value.push("city");
276
+ if (!p.country) errors.value.push("country");
277
+ if (!/^\d{3,4}$/.test(`${p.cvv}`)) errors.value.push("cvv");
278
+ const cleanCardNumber = p.cardNumber.replace(/\s+/g, "");
279
+ // Validate card number based on type and length
280
+ const isValidCard = cleanCardNumber.match(/^4\d{15}$/) || // Visa (16 digits)
281
+ cleanCardNumber.match(/^5[1-5]\d{14}$/) || // Mastercard (16 digits)
282
+ cleanCardNumber.match(/^3[47]\d{13}$/); // AMEX (15 digits)
283
+ if (!isValidCard) errors.value.push("cardNumber");
284
+ if (!p.expYear) errors.value.push("expYear");
285
+ if (!p.expMonth) errors.value.push("expMonth");
286
+ if (!p.expMonth) errors.value.push("expMonth");
287
+ return errors.value.length === 0;
288
+ };
289
+
290
+ const handleCreditCardUpdate = (event) => {
291
+ const value = event.target.value.replace(/\D/g, "");
292
+
293
+ // Determine max length based on card type
294
+ let maxLength = 16; // Default for Visa/Mastercard
295
+ if (value.startsWith('3')) {
296
+ maxLength = 15; // AMEX
297
+ } else if (value.startsWith('4') || value.startsWith('5')) {
298
+ maxLength = 16; // Visa/Mastercard
299
+ }
300
+
301
+ const subs = value.substring(0, maxLength);
302
+ const cardInfo = validateCard(subs);
303
+
304
+ // Store clean number (without spaces) in profile
305
+ profile.value.cardNumber = subs;
306
+ // Display formatted number in input
307
+ displayCardNumber.value = cardInfo.formatted;
308
+ };
309
+
310
+ function done() {
311
+ // Clear state if country is not US
312
+ if (profile.value.country !== 'US') {
313
+ profile.value.state = '';
314
+ }
315
+
316
+ ui.logger.Info("Created profile", profile.value);
317
+ if (validate(profile.value) !== true) return;
318
+ ui.toggleModal("");
319
+ ui.addProfile(profile.value);
320
+ }
321
+ </script>
@@ -0,0 +1,142 @@
1
+ <template>
2
+ <Row
3
+ class="relative text-white grid-cols-7 lg:grid-cols-8"
4
+ @click="ui.setOpenContextMenu('')"
5
+ @click.right.prevent="ui.setOpenContextMenu('')"
6
+ >
7
+ <div class="col-span-3 lg:col-span-2 flex">
8
+ <Checkbox
9
+ class="ml-0 mr-4"
10
+ :toggled="props.task.selected"
11
+ @valueUpdate="ui.toggleProfileSelected(props.task._id)"
12
+ />
13
+ <h4 class="mx-auto text-white">
14
+ {{ props.task.profileName }}
15
+ </h4>
16
+ </div>
17
+ <div class="col-span-1 lg:col-span-2">
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 class="w-6 h-6" :src="getAccountType()" />
27
+ </h4>
28
+ </div>
29
+ <div class="col-span-1">
30
+ <h4 class="text-white">{{ expDate() }}</h4>
31
+ </div>
32
+ <div class="col-span-1">
33
+ <h4 v-if="props.task.enabled" class="text-green-400 flex justify-center">
34
+ <img class="w-3 h-3 green" src="/img/controls/enable.svg" />
35
+ </h4>
36
+ <h4 v-else class="text-red-400 flex justify-center">
37
+ <img class="w-3 h-3 fill-red-400" src="/img/close.svg" />
38
+ </h4>
39
+ </div>
40
+
41
+ <div class="col-span-1 hidden lg:block">
42
+ <h4 class="text-white flex justify-center gap-1">
43
+ <TagLabel v-for="tag in props.task.tags" :key="tag" :text="tag" />
44
+ </h4>
45
+ </div>
46
+
47
+ <div class="col-span-1 flex">
48
+ <ul class="task-buttons">
49
+ <li>
50
+ <button @click="edit">
51
+ <EditIcon />
52
+ </button>
53
+ </li>
54
+ <li v-if="props.task.enabled">
55
+ <button @click="disable">
56
+ <img class="w-4 h-4" src="/img/controls/disable.svg" />
57
+ </button>
58
+ </li>
59
+ <li v-else>
60
+ <button @click="enable">
61
+ <img class="w-4 h-4" src="/img/controls/enable.svg" />
62
+ </button>
63
+ </li>
64
+ <li>
65
+ <button @click="deleteProfile"><TrashIcon /></button>
66
+ </li>
67
+ </ul>
68
+ </div>
69
+ </Row>
70
+ </template>
71
+ <style lang="scss" scoped>
72
+ .green svg path {
73
+ fill: rgb(68 183 68);
74
+ }
75
+
76
+ h4 {
77
+ @apply text-center;
78
+ }
79
+ .task-buttons {
80
+ @apply flex mx-auto gap-x-2;
81
+
82
+ button {
83
+ @apply p-1 rounded transition-colors hover:bg-dark-500;
84
+ }
85
+
86
+ svg {
87
+ @apply w-4 h-4;
88
+ }
89
+
90
+ img {
91
+ @apply w-4 h-4;
92
+ }
93
+ }
94
+
95
+ @media (max-width: 1024px) {
96
+ h4 {
97
+ font-size: 10px !important;
98
+ }
99
+ .task-buttons {
100
+ @apply gap-x-3;
101
+ }
102
+ .task-id {
103
+ font-size: 6px !important;
104
+ margin-right: -12px;
105
+ margin-top: 20px;
106
+ }
107
+ .task-id-alt {
108
+ font-size: 7px !important;
109
+ }
110
+ }
111
+ </style>
112
+ <script setup>
113
+ import { Row } from "@/components/Table";
114
+ import { PlayIcon, TrashIcon, BagWhiteIcon, PauseIcon, EditIcon } from "@/components/icons";
115
+ import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
116
+ import { useUIStore } from "@/stores/ui";
117
+ import { validateCard } from "@/stores/utils";
118
+ import TagLabel from "@/components/Editors/TagLabel.vue";
119
+
120
+ const ui = useUIStore();
121
+
122
+ const props = defineProps({
123
+ task: { type: Object }
124
+ });
125
+
126
+ const getAccountType = () => {
127
+ var cn = props.task.cardNumber;
128
+ if (cn.startsWith("4")) return `/img/banks/visa.svg`; // visa
129
+ else if (cn.startsWith("3")) return `/img/banks/amex.svg`; // amex
130
+ else if (cn.startsWith("5")) return `/img/banks/mastercard.svg`; // master
131
+ };
132
+
133
+ const expDate = () =>
134
+ props.task.privacy ? "••/••" : `${props.task.expMonth}/${props.task.expYear?.replace("20", "")}`;
135
+ const enable = async () => await ui.addProfile({ ...props.task, enabled: true });
136
+ const disable = async () => await ui.addProfile({ ...props.task, enabled: false });
137
+ const edit = () => {
138
+ ui.currentlyEditing = props.task;
139
+ ui.toggleModal("create-profile");
140
+ };
141
+ const deleteProfile = async () => await ui.deleteProfile(props.task._id);
142
+ </script>
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <div>
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
+ <div class="flex gap-3 justify-center">
6
+ <img class="w-5" :src="`/flags/${current?.toLowerCase()}.svg`" />
7
+ </div>
8
+ </span>
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
+ >
13
+ <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`" />
22
+ </div>
23
+ </div>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ </template>
28
+
29
+ <script setup>
30
+ import { ref, watch } from "vue";
31
+ const countries = ["US", "CA", "DE", "FR", "DK"];
32
+ const open = ref(false);
33
+
34
+ const props = defineProps({
35
+ value: { type: String },
36
+ onClick: { type: Function },
37
+ disabled: { type: Boolean, required: false }
38
+ });
39
+ const current = ref(props.value || "US");
40
+
41
+ const set = (c) => {
42
+ if (props.disabled) return;
43
+ current.value = c;
44
+ open.value = false;
45
+ props.onClick(c);
46
+ };
47
+
48
+ watch(
49
+ () => props.value,
50
+ (n) => (current.value = n)
51
+ );
52
+ </script>
53
+
54
+ <style scoped>
55
+ .special-dropdown {
56
+ @apply min-w-20;
57
+ }
58
+
59
+ .small-dropdown {
60
+ background-clip: border-box !important;
61
+ /* border-radius: 100% !important; */
62
+ padding: 0;
63
+ width: 3em !important;
64
+ /* height: 3em !important; */
65
+ display: flex;
66
+ justify-items: center;
67
+ justify-content: center;
68
+ }
69
+
70
+ .dropdown-content {
71
+ left: -1.1rem;
72
+ z-index: 99999999999999;
73
+ }
74
+ </style>
75
+ , watch
@@ -0,0 +1,96 @@
1
+ <template>
2
+ <Table>
3
+ <Header class="text-center grid-cols-7 lg:grid-cols-8">
4
+ <div class="col-span-3 lg:col-span-2 flex">
5
+ <Checkbox
6
+ class="mr-3"
7
+ :toggled="ui.mainCheckbox.profiles"
8
+ @valueUpdate="ui.toggleMainCheckbox('profiles')"
9
+ />
10
+ <div class="mx-auto flex items-center" @click="ui.toggleSort('eventId')">
11
+ <ProfileIcon class="mr-0 ipadlg:mr-3 w-4 h-4" />
12
+ <h4 class="hidden ipadlg:flex">Profile Name</h4>
13
+ </div>
14
+ </div>
15
+ <div class="lg:col-span-2 col-span-1 items-center justify-center flex" v-once>
16
+ <CartIcon class="mr-0 ipadlg:mr-3 w-4 h-4" />
17
+ <h4 class="hidden ipadlg:flex">Card Number</h4>
18
+ </div>
19
+ <div class="col-span-1 flex items-center justify-center" v-once>
20
+ <TimerIcon class="mr-0 ipadlg:mr-3 w-4 h-4" />
21
+ <h4 class="hidden ipadlg:flex">Exp. Date</h4>
22
+ </div>
23
+ <div class="col-span-1 flex items-center justify-center" v-once>
24
+ <CheckmarkIcon class="mr-0 ipadlg:mr-3 w-4 h-4" />
25
+ <h4 class="hidden ipadlg:flex">Enabled</h4>
26
+ </div>
27
+ <div class="col-span-1 hidden lg:flex items-center justify-center" v-once>
28
+ <TicketIcon class="mr-0 lg:mr-3 w-4 h-4" />
29
+ <h4 class="hidden lg:flex">Tags</h4>
30
+ </div>
31
+ <div class="col-span-1 flex items-center justify-center" v-once>
32
+ <ClickIcon class="mr-0 ipadlg:mr-3 w-4 h-4" />
33
+ <h4 class="hidden ipadlg:flex">Actions</h4>
34
+ </div>
35
+ </Header>
36
+ <div v-if="toRender.length != 0">
37
+ <RecycleScroller
38
+ :items="toRender"
39
+ :item-size="64"
40
+ key-field="index"
41
+ 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"
42
+ >
43
+ <template #default="props">
44
+ <div class="task" :key="i[props.item.index]">
45
+ <Profile
46
+ @click="i[props.item.index]++"
47
+ :class="[props.item.index % 2 == 1 ? 'bg-dark-500' : 'bg-dark-550']"
48
+ :task="props.item"
49
+ />
50
+ </div>
51
+ </template>
52
+ </RecycleScroller>
53
+ </div>
54
+ <div v-else class="flex justify-center text-light-400 py-2 bg-dark-500 border-b-2 border-border">
55
+ No profiles found
56
+ </div>
57
+ </Table>
58
+ </template>
59
+ <style lang="scss" scoped>
60
+ .task {
61
+ height: 64px;
62
+ }
63
+ h4 {
64
+ @apply text-white;
65
+ }
66
+
67
+ .stop-pan {
68
+ touch-action: pan-y pan-up pan-down;
69
+ }
70
+
71
+ .max-h-big-prof {
72
+ max-height: calc(100vh - 20rem);
73
+ overflow: hidden;
74
+ }
75
+ </style>
76
+ <script setup>
77
+ import { Table, Header } from "@/components/Table";
78
+ import { CartIcon, TicketIcon, ClickIcon, TimerIcon, ProfileIcon, CheckmarkIcon } from "@/components/icons";
79
+ import Profile from "./Profile.vue";
80
+ import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
81
+ import { useUIStore } from "@/stores/ui";
82
+ import { computed, ref } from "vue";
83
+
84
+ const props = defineProps({
85
+ tasks: { type: Object }
86
+ });
87
+ const i = ref({});
88
+ props.tasks.forEach((t) => (i.value[t._id] = 0));
89
+
90
+ const ui = useUIStore();
91
+
92
+ const toRender = computed(() => {
93
+ let c = 0;
94
+ return props.tasks.map((t) => ({ ...t, index: c++ }));
95
+ });
96
+ </script>
@@ -0,0 +1,16 @@
1
+ <template>
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
+ </div>
5
+ </template>
6
+
7
+ <style scoped>
8
+ .small {
9
+ font-size: 0.6rem;
10
+ line-height: 0.8rem;
11
+ }
12
+ </style>
13
+
14
+ <script setup>
15
+ const props = defineProps({ text: { type: String } });
16
+ </script>