@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.
- package/.claude/settings.local.json +45 -0
- package/.eslintrc.js +24 -0
- package/.prettierignore +1 -0
- package/.prettierrc +10 -0
- package/.vscode/extensions.json +3 -0
- package/ICONS.md +21 -0
- package/README.md +65 -0
- package/backend/api.js +430 -0
- package/backend/auth.js +62 -0
- package/backend/batching.js +43 -0
- package/backend/endpoints.js +343 -0
- package/backend/index.js +23 -0
- package/backend/mock-data.js +66 -0
- package/backend/mock-src/classes/logger.js +112 -0
- package/backend/mock-src/classes/utils.js +42 -0
- package/backend/mock-src/ticketmaster.js +92 -0
- package/backend/validator.js +62 -0
- package/config/configs.json +20 -0
- package/config/filter.json +3 -0
- package/config/presale.csv +3 -0
- package/config/proxies.txt +6 -0
- package/config/used-codes.json +4 -0
- package/index.html +114 -0
- package/index.js +2 -0
- package/jsconfig.json +16 -0
- package/package.json +48 -0
- package/postcss.config.js +6 -0
- package/postinstall.js +9 -0
- package/public/android-chrome-192x192.png +0 -0
- package/public/android-chrome-512x512.png +0 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/flags/ae.svg +1 -0
- package/public/flags/at.svg +1 -0
- package/public/flags/au.svg +1 -0
- package/public/flags/be.svg +1 -0
- package/public/flags/ch.svg +1 -0
- package/public/flags/cz.svg +1 -0
- package/public/flags/de.svg +1 -0
- package/public/flags/dk.svg +1 -0
- package/public/flags/es.svg +1 -0
- package/public/flags/nl.svg +1 -0
- package/public/flags/no.svg +1 -0
- package/public/flags/nz.svg +1 -0
- package/public/flags/pl.svg +1 -0
- package/public/flags/se.svg +1 -0
- package/public/flags/uk.svg +1 -0
- package/public/flags/us.svg +1 -0
- package/public/img/award.svg +3 -0
- package/public/img/background.svg +14 -0
- package/public/img/bag_w.svg +12 -0
- package/public/img/banks/amex.svg +4 -0
- package/public/img/banks/mastercard.svg +4 -0
- package/public/img/banks/visa.svg +4 -0
- package/public/img/camera.svg +3 -0
- package/public/img/close.svg +3 -0
- package/public/img/controls/disable.svg +5 -0
- package/public/img/controls/enable.svg +5 -0
- package/public/img/groups.svg +3 -0
- package/public/img/hand.svg +3 -0
- package/public/img/key.svg +3 -0
- package/public/img/logo.png +0 -0
- package/public/img/logo_icon.png +0 -0
- package/public/img/logo_icon_2.png +0 -0
- package/public/img/logo_trans.png +0 -0
- package/public/img/loyalty.svg +3 -0
- package/public/img/mail.svg +3 -0
- package/public/img/pencil.svg +3 -0
- package/public/img/profile.svg +4 -0
- package/public/img/reload.svg +3 -0
- package/public/img/sandclock.svg +25 -0
- package/public/img/save.svg +5 -0
- package/public/img/savings.svg +3 -0
- package/public/img/scanner.svg +3 -0
- package/public/img/sell.svg +3 -0
- package/public/img/shield.svg +3 -0
- package/public/img/ski.svg +3 -0
- package/public/img/stadium.svg +8 -0
- package/public/img/stadium_w.svg +8 -0
- package/public/img/timer.svg +3 -0
- package/public/manifest.json +27 -0
- package/public/robots.txt +2 -0
- package/run +10 -0
- package/src/App.vue +307 -0
- package/src/assets/css/_input.scss +197 -0
- package/src/assets/css/main.scss +269 -0
- package/src/assets/css/tailwind.css +3 -0
- package/src/assets/img/award.svg +3 -0
- package/src/assets/img/background.svg +11 -0
- package/src/assets/img/camera.svg +3 -0
- package/src/assets/img/close.svg +3 -0
- package/src/assets/img/eyes/closed.svg +13 -0
- package/src/assets/img/eyes/open.svg +12 -0
- package/src/assets/img/groups.svg +3 -0
- package/src/assets/img/hand.svg +3 -0
- package/src/assets/img/key.svg +3 -0
- package/src/assets/img/logo.png +0 -0
- package/src/assets/img/logo_icon.png +0 -0
- package/src/assets/img/logo_icon_2.png +0 -0
- package/src/assets/img/logo_trans.png +0 -0
- package/src/assets/img/loyalty.svg +3 -0
- package/src/assets/img/mail.svg +3 -0
- package/src/assets/img/pencil.svg +3 -0
- package/src/assets/img/reload.svg +3 -0
- package/src/assets/img/savings.svg +3 -0
- package/src/assets/img/scanner.svg +3 -0
- package/src/assets/img/sell.svg +3 -0
- package/src/assets/img/shield.svg +3 -0
- package/src/assets/img/ski.svg +3 -0
- package/src/assets/img/square_check.svg +5 -0
- package/src/assets/img/square_uncheck.svg +5 -0
- package/src/assets/img/stadium.svg +8 -0
- package/src/assets/img/timer.svg +3 -0
- package/src/assets/img/wildcard.svg +7 -0
- package/src/components/Auth/LoginForm.vue +48 -0
- package/src/components/Editors/Account/Account.vue +119 -0
- package/src/components/Editors/Account/AccountCreator.vue +147 -0
- package/src/components/Editors/Account/AccountView.vue +87 -0
- package/src/components/Editors/Account/CreateAccount.vue +106 -0
- package/src/components/Editors/Profile/CreateProfile.vue +321 -0
- package/src/components/Editors/Profile/Profile.vue +142 -0
- package/src/components/Editors/Profile/ProfileCountryChooser.vue +75 -0
- package/src/components/Editors/Profile/ProfileView.vue +96 -0
- package/src/components/Editors/TagLabel.vue +16 -0
- package/src/components/Editors/TagToggle.vue +41 -0
- package/src/components/Filter/Filter.vue +409 -0
- package/src/components/Filter/FilterPreview.vue +236 -0
- package/src/components/Filter/PriceSortToggle.vue +105 -0
- package/src/components/Table/Header.vue +5 -0
- package/src/components/Table/Row.vue +5 -0
- package/src/components/Table/Table.vue +14 -0
- package/src/components/Table/index.js +4 -0
- package/src/components/Tasks/CheckStock.vue +62 -0
- package/src/components/Tasks/Controls/DesktopControls.vue +73 -0
- package/src/components/Tasks/Controls/MobileControls.vue +32 -0
- package/src/components/Tasks/Controls/index.js +3 -0
- package/src/components/Tasks/CreateTaskAXS.vue +339 -0
- package/src/components/Tasks/CreateTaskTM.vue +459 -0
- package/src/components/Tasks/MassEdit.vue +50 -0
- package/src/components/Tasks/QuickSettings.vue +167 -0
- package/src/components/Tasks/ScrapeVenue.vue +42 -0
- package/src/components/Tasks/Stats.vue +66 -0
- package/src/components/Tasks/Task.vue +296 -0
- package/src/components/Tasks/TaskLabel.vue +20 -0
- package/src/components/Tasks/TaskView.vue +126 -0
- package/src/components/Tasks/Utilities.vue +33 -0
- package/src/components/icons/Award.vue +8 -0
- package/src/components/icons/Bag.vue +8 -0
- package/src/components/icons/BagWhite.vue +8 -0
- package/src/components/icons/Box.vue +8 -0
- package/src/components/icons/Camera.vue +8 -0
- package/src/components/icons/Cart.vue +8 -0
- package/src/components/icons/Check.vue +5 -0
- package/src/components/icons/Checkmark.vue +11 -0
- package/src/components/icons/Click.vue +8 -0
- package/src/components/icons/Close.vue +21 -0
- package/src/components/icons/CloseX.vue +5 -0
- package/src/components/icons/Console.vue +13 -0
- package/src/components/icons/Down.vue +8 -0
- package/src/components/icons/Edit.vue +13 -0
- package/src/components/icons/Event.vue +8 -0
- package/src/components/icons/Expand.vue +8 -0
- package/src/components/icons/Filter.vue +13 -0
- package/src/components/icons/Gear.vue +8 -0
- package/src/components/icons/Group.vue +8 -0
- package/src/components/icons/Hand.vue +8 -0
- package/src/components/icons/Key.vue +21 -0
- package/src/components/icons/Logout.vue +13 -0
- package/src/components/icons/Loyalty.vue +8 -0
- package/src/components/icons/Mail.vue +8 -0
- package/src/components/icons/Menu.vue +8 -0
- package/src/components/icons/Pause.vue +5 -0
- package/src/components/icons/Pencil.vue +21 -0
- package/src/components/icons/Play.vue +8 -0
- package/src/components/icons/Plus.vue +8 -0
- package/src/components/icons/Profile.vue +18 -0
- package/src/components/icons/Reload.vue +7 -0
- package/src/components/icons/Sandclock.vue +33 -0
- package/src/components/icons/Savings.vue +8 -0
- package/src/components/icons/Scanner.vue +8 -0
- package/src/components/icons/Scrape.vue +8 -0
- package/src/components/icons/Sell.vue +21 -0
- package/src/components/icons/Shield.vue +8 -0
- package/src/components/icons/Shrink.vue +8 -0
- package/src/components/icons/Ski.vue +8 -0
- package/src/components/icons/Spinner.vue +42 -0
- package/src/components/icons/SquareCheck.vue +18 -0
- package/src/components/icons/SquareUncheck.vue +18 -0
- package/src/components/icons/Stadium.vue +13 -0
- package/src/components/icons/StadiumWhite.vue +13 -0
- package/src/components/icons/Status.vue +8 -0
- package/src/components/icons/Tag.vue +8 -0
- package/src/components/icons/Tasks.vue +13 -0
- package/src/components/icons/Ticket.vue +8 -0
- package/src/components/icons/Timer.vue +8 -0
- package/src/components/icons/Trash.vue +8 -0
- package/src/components/icons/Up.vue +10 -0
- package/src/components/icons/Wildcard.vue +18 -0
- package/src/components/icons/index.js +111 -0
- package/src/components/ui/Modal.vue +61 -0
- package/src/components/ui/Navbar.vue +207 -0
- package/src/components/ui/ReconnectIndicator.vue +90 -0
- package/src/components/ui/Splash.vue +24 -0
- package/src/components/ui/controls/CountryChooser.vue +87 -0
- package/src/components/ui/controls/EyeToggle.vue +11 -0
- package/src/components/ui/controls/atomic/Checkbox.vue +28 -0
- package/src/components/ui/controls/atomic/Dropdown.vue +138 -0
- package/src/components/ui/controls/atomic/LoadingButton.vue +45 -0
- package/src/components/ui/controls/atomic/MultiDropdown.vue +262 -0
- package/src/components/ui/controls/atomic/Switch.vue +84 -0
- package/src/libs/Filter.js +593 -0
- package/src/libs/ansii.js +565 -0
- package/src/libs/panzoom.js +1413 -0
- package/src/main.js +23 -0
- package/src/registerServiceWorker.js +32 -0
- package/src/router/index.js +65 -0
- package/src/stores/cities.json +1 -0
- package/src/stores/connection.js +399 -0
- package/src/stores/countries.js +88 -0
- package/src/stores/logger.js +103 -0
- package/src/stores/requests.js +88 -0
- package/src/stores/sampleData.js +1034 -0
- package/src/stores/ui.js +584 -0
- package/src/stores/utils.js +554 -0
- package/src/types/index.js +42 -0
- package/src/utils/debug.js +1 -0
- package/src/views/Accounts.vue +191 -0
- package/src/views/Console.vue +224 -0
- package/src/views/Editor.vue +785 -0
- package/src/views/FilterBuilder.vue +785 -0
- package/src/views/Login.vue +27 -0
- package/src/views/Profiles.vue +209 -0
- package/src/views/Tasks.vue +157 -0
- package/static/offline.html +184 -0
- package/tailwind.config.js +57 -0
- package/vite.config.js +63 -0
- package/vue.config.js +32 -0
- 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>
|