@necrolab/dashboard 0.5.15 → 0.5.17
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/backend/api.js +2 -3
- package/eslint.config.js +46 -0
- package/index.html +2 -1
- package/package.json +5 -2
- package/src/App.vue +70 -566
- package/src/assets/css/base/mixins.scss +72 -0
- package/src/assets/css/base/reset.scss +0 -2
- package/src/assets/css/base/scroll.scss +43 -36
- package/src/assets/css/base/typography.scss +9 -10
- package/src/assets/css/base/variables.scss +43 -0
- package/src/assets/css/components/accessibility.scss +37 -0
- package/src/assets/css/components/buttons.scss +61 -74
- package/src/assets/css/components/forms.scss +31 -32
- package/src/assets/css/components/headers.scss +13 -21
- package/src/assets/css/components/modals.scss +2 -2
- package/src/assets/css/components/search-groups.scss +28 -22
- package/src/assets/css/components/tables.scss +5 -7
- package/src/assets/css/components/toasts.scss +7 -7
- package/src/assets/css/components/utilities.scss +295 -0
- package/src/assets/css/main.scss +55 -139
- package/src/components/Auth/LoginForm.vue +7 -86
- package/src/components/Console/ConsoleToolbar.vue +123 -0
- package/src/components/Editors/Account/Account.vue +12 -12
- package/src/components/Editors/Account/AccountView.vue +38 -111
- package/src/components/Editors/Account/CreateAccount.vue +11 -61
- package/src/components/Editors/Account/{AccountCreator.vue → CreateAccountBatch.vue} +28 -59
- package/src/components/Editors/AdminFileEditor.vue +179 -0
- package/src/components/Editors/Profile/CreateProfile.vue +77 -150
- package/src/components/Editors/Profile/Profile.vue +20 -21
- package/src/components/Editors/Profile/ProfileCountryChooser.vue +16 -60
- package/src/components/Editors/Profile/ProfileView.vue +41 -116
- package/src/components/Editors/ProxyFileEditor.vue +86 -0
- package/src/components/Editors/TagLabel.vue +16 -55
- package/src/components/Editors/TagToggle.vue +20 -8
- package/src/components/Filter/Filter.vue +66 -79
- package/src/components/Filter/FilterPreview.vue +153 -135
- package/src/components/Filter/PriceSortToggle.vue +36 -43
- package/src/components/Table/Header.vue +1 -1
- package/src/components/Table/Table.vue +45 -51
- package/src/components/Tasks/CheckStock.vue +7 -16
- package/src/components/Tasks/Controls/DesktopControls.vue +15 -60
- package/src/components/Tasks/Controls/MobileControls.vue +5 -20
- package/src/components/Tasks/CreateTaskAXS.vue +20 -118
- package/src/components/Tasks/CreateTaskTM.vue +33 -189
- package/src/components/Tasks/EventDetailRow.vue +21 -0
- package/src/components/Tasks/MassEdit.vue +6 -16
- package/src/components/Tasks/QuickSettings.vue +140 -216
- package/src/components/Tasks/ScrapeVenue.vue +4 -13
- package/src/components/Tasks/Stats.vue +20 -39
- package/src/components/Tasks/Task.vue +64 -270
- package/src/components/Tasks/TaskLabel.vue +9 -3
- package/src/components/Tasks/TaskView.vue +45 -64
- package/src/components/Tasks/Utilities.vue +10 -44
- package/src/components/Tasks/ViewTask.vue +23 -107
- package/src/components/icons/Close.vue +2 -8
- package/src/components/icons/Gear.vue +8 -8
- package/src/components/icons/Hash.vue +5 -0
- package/src/components/icons/Key.vue +2 -8
- package/src/components/icons/Pencil.vue +2 -8
- package/src/components/icons/Profile.vue +2 -8
- package/src/components/icons/Sell.vue +2 -8
- package/src/components/icons/Spinner.vue +4 -7
- package/src/components/icons/Wildcard.vue +2 -8
- package/src/components/icons/index.js +3 -5
- package/src/components/ui/ActionButtonGroup.vue +113 -52
- package/src/components/ui/BalanceIndicator.vue +60 -0
- package/src/components/ui/EmptyState.vue +24 -0
- package/src/components/ui/EnableDisableToggle.vue +23 -0
- package/src/components/ui/FormField.vue +49 -49
- package/src/components/ui/IconLabel.vue +23 -0
- package/src/components/ui/InfoRow.vue +21 -54
- package/src/components/ui/Modal.vue +161 -54
- package/src/components/ui/Navbar.vue +63 -44
- package/src/components/ui/ReadonlyFieldsSection.vue +31 -0
- package/src/components/ui/ReconnectIndicator.vue +111 -124
- package/src/components/ui/SectionCard.vue +6 -14
- package/src/components/ui/Splash.vue +2 -10
- package/src/components/ui/StatusBadge.vue +26 -28
- package/src/components/ui/TaskToggle.vue +54 -0
- package/src/components/ui/controls/CountryChooser.vue +29 -66
- package/src/components/ui/controls/EyeToggle.vue +1 -1
- package/src/components/ui/controls/atomic/Checkbox.vue +40 -121
- package/src/components/ui/controls/atomic/Dropdown.vue +103 -139
- package/src/components/ui/controls/atomic/MultiDropdown.vue +72 -120
- package/src/components/ui/controls/atomic/Switch.vue +21 -84
- package/src/composables/useCodeEditor.js +117 -0
- package/src/composables/useColorMapping.js +15 -0
- package/src/composables/useCopyToClipboard.js +1 -1
- package/src/composables/useDateFormatting.js +21 -0
- package/src/composables/useDeviceDetection.js +14 -0
- package/src/composables/useDropdownPosition.js +1 -4
- package/src/composables/useDynamicTableHeight.js +31 -0
- package/src/composables/useEnableDisable.js +6 -0
- package/src/composables/useFilterCSS.js +71 -0
- package/src/composables/useFormValidation.js +92 -0
- package/src/composables/useGetAllTags.js +9 -0
- package/src/composables/useIOSViewportHandling.js +76 -0
- package/src/composables/useNotchHandling.js +306 -0
- package/src/composables/useRowSelection.js +0 -3
- package/src/composables/useTableRender.js +23 -0
- package/src/composables/useTicketPricing.js +16 -0
- package/src/composables/useWindowDimensions.js +21 -0
- package/src/composables/useZoomPrevention.js +96 -0
- package/src/constants/tableLayout.js +14 -0
- package/src/libs/Filter.js +14 -20
- package/src/libs/panzoom.js +1 -5
- package/src/libs/utils/array.js +58 -0
- package/src/{stores/utils.js → libs/utils/dataGeneration.js} +2 -250
- package/src/libs/utils/eventUrl.js +40 -0
- package/src/libs/utils/string.js +3 -0
- package/src/libs/utils/time.js +20 -0
- package/src/libs/utils/validation.js +64 -0
- package/src/main.js +0 -2
- package/src/stores/connection.js +1 -29
- package/src/stores/logger.js +6 -12
- package/src/stores/sampleData.js +1 -2
- package/src/stores/ui.js +80 -71
- package/src/utils/tableHelpers.js +1 -0
- package/src/views/Accounts.vue +19 -38
- package/src/views/Console.vue +74 -253
- package/src/views/Editor.vue +47 -1114
- package/src/views/FilterBuilder.vue +190 -461
- package/src/views/Login.vue +3 -28
- package/src/views/Profiles.vue +17 -32
- package/src/views/Tasks.vue +51 -38
- package/tailwind.config.js +82 -71
- package/workbox-config.cjs +47 -5
- package/docs/plans/2026-02-08-tailwind-consolidation.md +0 -2438
- package/exit +0 -209
- package/run +0 -177
- package/src/assets/css/base/color-fallbacks.scss +0 -10
- package/src/assets/img/background.svg.backup +0 -11
- package/src/components/icons/SquareCheck.vue +0 -18
- package/src/components/icons/SquareUncheck.vue +0 -18
- package/src/components/ui/controls/atomic/LoadingButton.vue +0 -45
- package/switch-branch.sh +0 -41
- /package/public/{reconnect-logo.png → img/reconnect-logo.png} +0 -0
|
@@ -9,21 +9,17 @@
|
|
|
9
9
|
<div>
|
|
10
10
|
<div class="my-3 grid grid-cols-12 gap-3">
|
|
11
11
|
<!-- Profile tag -->
|
|
12
|
-
<
|
|
13
|
-
<label class="label-override mb-2">
|
|
14
|
-
Profile Tag
|
|
15
|
-
<TagIcon />
|
|
16
|
-
</label>
|
|
12
|
+
<FormField label="Profile Tag" :icon="TagIcon" z-index="0" class="col-span-12 md:col-span-4" noWrapper>
|
|
17
13
|
<Dropdown
|
|
18
14
|
:class="`input-default dropdown w-full`"
|
|
19
15
|
:default="ui.profile.tags[0]"
|
|
20
16
|
:options="ui.profile.tags"
|
|
21
|
-
:onClick="(f) => (
|
|
17
|
+
:onClick="(f) => (formProfile.tag = f)"
|
|
22
18
|
:capitalize="true" />
|
|
23
|
-
</
|
|
19
|
+
</FormField>
|
|
24
20
|
|
|
25
21
|
<!-- Card Number -->
|
|
26
|
-
<FormField label="Card Number" :icon="CartIcon" :error="errors.includes('cardNumber')" z-index="0" class="col-span-8">
|
|
22
|
+
<FormField label="Card Number" :icon="CartIcon" :error="errors.includes('cardNumber')" z-index="0" class="col-span-12 md:col-span-8">
|
|
27
23
|
<input
|
|
28
24
|
ref="cardNumberInput"
|
|
29
25
|
placeholder="Enter card number"
|
|
@@ -35,51 +31,39 @@
|
|
|
35
31
|
</FormField>
|
|
36
32
|
|
|
37
33
|
<!-- Country chooser -->
|
|
38
|
-
<
|
|
39
|
-
<label class="label-override mb-2">
|
|
40
|
-
Country
|
|
41
|
-
<StadiumIcon />
|
|
42
|
-
</label>
|
|
34
|
+
<FormField label="Country" :icon="StadiumIcon" z-index="0" class="col-span-12 md:col-span-2" noWrapper>
|
|
43
35
|
<ProfileCountryChooser
|
|
44
|
-
class="h-
|
|
45
|
-
:value="
|
|
36
|
+
class="h-10"
|
|
37
|
+
:value="formProfile.country"
|
|
46
38
|
:onClick="chooseCountry"
|
|
47
39
|
:disabled="true" />
|
|
48
|
-
</
|
|
40
|
+
</FormField>
|
|
49
41
|
|
|
50
42
|
<!-- Exp Year -->
|
|
51
|
-
<
|
|
52
|
-
<label class="label-override mb-2">
|
|
53
|
-
Expiry Year
|
|
54
|
-
<TimerIcon />
|
|
55
|
-
</label>
|
|
43
|
+
<FormField label="Expiry Year" :icon="TimerIcon" :error="errors.includes('expYear')" z-index="0" class="col-span-6 md:col-span-5" noWrapper>
|
|
56
44
|
<Dropdown
|
|
57
45
|
:class="`input-default dropdown w-full ${errors.includes('expYear') ? 'error' : ''}`"
|
|
58
46
|
default="Expiry Year"
|
|
59
47
|
:value="
|
|
60
|
-
|
|
61
|
-
? '20' +
|
|
62
|
-
:
|
|
63
|
-
?
|
|
48
|
+
formProfile.expYear && !formProfile?.expYear?.startsWith('20')
|
|
49
|
+
? '20' + formProfile.expYear
|
|
50
|
+
: formProfile.expYear
|
|
51
|
+
? formProfile.expYear
|
|
64
52
|
: undefined
|
|
65
53
|
"
|
|
66
54
|
:options="['2025', '2026', '2027', '2028', '2029', '2030', '2031']"
|
|
67
|
-
:onClick="(f) => (
|
|
68
|
-
</
|
|
55
|
+
:onClick="(f) => (formProfile.expYear = f)" />
|
|
56
|
+
</FormField>
|
|
69
57
|
|
|
70
58
|
<!-- Exp Month -->
|
|
71
|
-
<
|
|
72
|
-
<label class="label-override mb-2">
|
|
73
|
-
Expiry Month
|
|
74
|
-
<TimerIcon />
|
|
75
|
-
</label>
|
|
59
|
+
<FormField label="Expiry Month" :icon="TimerIcon" :error="errors.includes('expMonth')" z-index="0" class="col-span-6 md:col-span-5" noWrapper>
|
|
76
60
|
<Dropdown
|
|
77
61
|
:class="`input-default dropdown w-full ${errors.includes('expMonth') ? 'error' : ''}`"
|
|
78
62
|
default="Expiry Month"
|
|
79
|
-
:value="
|
|
63
|
+
:value="formProfile.expMonth ? formProfile.expMonth : undefined"
|
|
80
64
|
:options="['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']"
|
|
81
|
-
:onClick="(f) => (
|
|
82
|
-
</
|
|
65
|
+
:onClick="(f) => (formProfile.expMonth = f)" />
|
|
66
|
+
</FormField>
|
|
83
67
|
|
|
84
68
|
<!-- CVV -->
|
|
85
69
|
<FormField label="CVV" :icon="ShieldIcon" :error="errors.includes('cvv')" z-index="0" class="col-span-6 md:col-span-4">
|
|
@@ -89,55 +73,45 @@
|
|
|
89
73
|
max="9999"
|
|
90
74
|
maxlength="4"
|
|
91
75
|
minlength="3"
|
|
92
|
-
v-model="
|
|
76
|
+
v-model="formProfile.cvv" />
|
|
93
77
|
</FormField>
|
|
94
78
|
|
|
95
79
|
<!-- City -->
|
|
96
80
|
<FormField label="City" :icon="StadiumIcon" :error="errors.includes('city')" z-index="0" class="col-span-6 md:col-span-4">
|
|
97
|
-
<input placeholder="Denver" v-model="
|
|
81
|
+
<input placeholder="Denver" v-model="formProfile.city" />
|
|
98
82
|
</FormField>
|
|
99
83
|
|
|
100
84
|
<!-- State -->
|
|
101
|
-
<
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
:options="usStates"
|
|
112
|
-
:allowDefault="false"
|
|
113
|
-
rightAmount="right-2"
|
|
114
|
-
:value="profile.state" />
|
|
115
|
-
</div>
|
|
85
|
+
<FormField label="State" :icon="SandclockIcon" z-index="0" class="col-span-6 md:col-span-4" noWrapper>
|
|
86
|
+
<Dropdown
|
|
87
|
+
v-if="formProfile.country === 'US'"
|
|
88
|
+
class="input-default w-full"
|
|
89
|
+
default="Select State"
|
|
90
|
+
:onClick="(state) => (formProfile.state = state)"
|
|
91
|
+
:options="usStates"
|
|
92
|
+
:allowDefault="false"
|
|
93
|
+
rightAmount="right-2"
|
|
94
|
+
:value="formProfile.state" />
|
|
116
95
|
<div v-else class="input-default">
|
|
117
96
|
<input disabled placeholder="N/A" value="" />
|
|
118
97
|
</div>
|
|
119
|
-
</
|
|
98
|
+
</FormField>
|
|
120
99
|
|
|
121
100
|
<!-- Zip -->
|
|
122
101
|
<FormField label="Zip" :icon="KeyIcon" :error="errors.includes('zipCode')" z-index="0" class="col-span-6 md:col-span-4">
|
|
123
|
-
<input placeholder="10005" type="number" min="1" max="12" v-model="
|
|
102
|
+
<input placeholder="10005" type="number" min="1" max="12" v-model="formProfile.zipCode" />
|
|
124
103
|
</FormField>
|
|
125
104
|
|
|
126
105
|
<!-- Address -->
|
|
127
106
|
<FormField label="Address" :icon="HandIcon" :error="errors.includes('address')" z-index="0" class="col-span-6 md:col-span-4">
|
|
128
|
-
<input placeholder="100 5th Avenue" v-model="
|
|
107
|
+
<input placeholder="100 5th Avenue" v-model="formProfile.address" />
|
|
129
108
|
</FormField>
|
|
130
109
|
|
|
131
110
|
<!-- Generate -->
|
|
132
|
-
<
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
</label>
|
|
137
|
-
<div class="input-default mt-2 flex h-10 w-full items-center">
|
|
138
|
-
<button
|
|
139
|
-
@click="generate"
|
|
140
|
-
class="flex w-full items-center justify-center gap-2 text-xs text-white">
|
|
111
|
+
<FormField label="Fake ID" :icon="WildcardIcon" z-index="0" class="col-span-6 md:col-span-4">
|
|
112
|
+
<button
|
|
113
|
+
@click="generate"
|
|
114
|
+
class="flex h-full w-full items-center justify-center gap-2 text-xs text-white">
|
|
141
115
|
<svg
|
|
142
116
|
xmlns="http://www.w3.org/2000/svg"
|
|
143
117
|
width="14"
|
|
@@ -153,81 +127,52 @@
|
|
|
153
127
|
<line x1="17" y1="2" x2="17" y2="6" />
|
|
154
128
|
</svg>
|
|
155
129
|
<span>Generate</span>
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
</div>
|
|
130
|
+
</button>
|
|
131
|
+
</FormField>
|
|
159
132
|
</div>
|
|
160
133
|
|
|
161
134
|
<!-- Readonly fields when editing -->
|
|
162
|
-
<
|
|
163
|
-
<div v-if="ui.currentlyEditing.tags && ui.currentlyEditing.tags.length > 0" class="col-span-6">
|
|
164
|
-
<label class="label-override mb-2">Tags</label>
|
|
165
|
-
<div class="flex gap-2 flex-wrap">
|
|
166
|
-
<TagLabel v-for="tag in ui.currentlyEditing.tags" :key="tag" :text="tag" />
|
|
167
|
-
</div>
|
|
168
|
-
</div>
|
|
169
|
-
<div class="col-span-6">
|
|
170
|
-
<label class="label-override mb-2">Status</label>
|
|
171
|
-
<div class="flex items-center gap-3 h-10">
|
|
172
|
-
<StatusBadge :enabled="ui.currentlyEditing.enabled" size="large" />
|
|
173
|
-
<span class="text-sm font-medium" :class="ui.currentlyEditing.enabled ? 'text-green-400' : 'text-red-400'">
|
|
174
|
-
{{ ui.currentlyEditing.enabled ? 'Enabled' : 'Disabled' }}
|
|
175
|
-
</span>
|
|
176
|
-
</div>
|
|
177
|
-
</div>
|
|
178
|
-
</div>
|
|
135
|
+
<ReadonlyFieldsSection v-if="ui.currentlyEditing?.profileName" :data="ui.currentlyEditing" />
|
|
179
136
|
</div>
|
|
180
137
|
|
|
181
|
-
<button
|
|
182
|
-
class="button-default ml-auto mt-4 flex w-48 items-center justify-center gap-x-2 bg-dark-400 text-xs"
|
|
183
|
-
@click="done()">
|
|
138
|
+
<button class="btn-modal ml-auto mt-4 w-48" @click="done()">
|
|
184
139
|
Save
|
|
185
|
-
<EditIcon />
|
|
140
|
+
<EditIcon class="ml-2" />
|
|
186
141
|
</button>
|
|
187
142
|
</Modal>
|
|
188
143
|
</template>
|
|
189
|
-
<style lang="scss" scoped>
|
|
190
|
-
.label-override {
|
|
191
|
-
@apply flex items-center;
|
|
192
|
-
color: #e1e1e4 !important;
|
|
193
|
-
|
|
194
|
-
svg {
|
|
195
|
-
@apply ml-2;
|
|
196
|
-
|
|
197
|
-
path {
|
|
198
|
-
fill: #e1e1e4 !important;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
</style>
|
|
203
144
|
<script setup>
|
|
204
145
|
import Modal from "@/components/ui/Modal.vue";
|
|
205
146
|
import FormField from "@/components/ui/FormField.vue";
|
|
206
147
|
import {
|
|
207
|
-
MailIcon,
|
|
208
148
|
CartIcon,
|
|
209
149
|
ShieldIcon,
|
|
210
150
|
StadiumIcon,
|
|
211
151
|
KeyIcon,
|
|
212
152
|
HandIcon,
|
|
213
|
-
ProfileIcon,
|
|
214
153
|
SandclockIcon,
|
|
215
154
|
TimerIcon,
|
|
216
155
|
TagIcon,
|
|
217
156
|
WildcardIcon
|
|
218
157
|
} from "@/components/icons";
|
|
219
158
|
import { EditIcon } from "@/components/icons";
|
|
220
|
-
import
|
|
221
|
-
import TagLabel from "@/components/Editors/TagLabel.vue";
|
|
159
|
+
import ReadonlyFieldsSection from "@/components/ui/ReadonlyFieldsSection.vue";
|
|
222
160
|
import ProfileCountryChooser from "@/components/Editors/Profile/ProfileCountryChooser.vue";
|
|
223
161
|
import { useUIStore } from "@/stores/ui";
|
|
224
162
|
import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
|
|
225
|
-
import { ref,
|
|
226
|
-
import { fakeId } from "@/
|
|
227
|
-
import { validateCard } from "@/
|
|
163
|
+
import { ref, watch, nextTick, onMounted } from "vue";
|
|
164
|
+
import { fakeId } from "@/libs/utils/dataGeneration";
|
|
165
|
+
import { validateCard } from "@/libs/utils/validation";
|
|
166
|
+
import { useFormValidation } from "@/composables/useFormValidation";
|
|
167
|
+
|
|
168
|
+
defineProps({
|
|
169
|
+
profile: {
|
|
170
|
+
type: Object,
|
|
171
|
+
default: null
|
|
172
|
+
}
|
|
173
|
+
});
|
|
228
174
|
|
|
229
|
-
const
|
|
230
|
-
const errors = ref([]);
|
|
175
|
+
const { errors, validateProfile } = useFormValidation();
|
|
231
176
|
const ui = useUIStore();
|
|
232
177
|
|
|
233
178
|
// US States list (excluding GA and PR, alphabetically ordered)
|
|
@@ -283,7 +228,7 @@ const usStates = [
|
|
|
283
228
|
"WY"
|
|
284
229
|
];
|
|
285
230
|
const cardNumberInput = ref(null);
|
|
286
|
-
const
|
|
231
|
+
const formProfile = ref({
|
|
287
232
|
cvv: "",
|
|
288
233
|
cardNumber: "",
|
|
289
234
|
city: "",
|
|
@@ -293,15 +238,15 @@ const profile = ref({
|
|
|
293
238
|
zipCode: ""
|
|
294
239
|
});
|
|
295
240
|
|
|
296
|
-
if (ui.currentlyEditing?.profileName)
|
|
241
|
+
if (ui.currentlyEditing?.profileName) formProfile.value = ui.currentlyEditing;
|
|
297
242
|
|
|
298
243
|
// Reactive display value for the formatted card number
|
|
299
244
|
const displayCardNumber = ref("");
|
|
300
245
|
|
|
301
246
|
// Initialize display card number with formatting
|
|
302
247
|
const initializeCardNumberDisplay = () => {
|
|
303
|
-
if (
|
|
304
|
-
const cleanNumber =
|
|
248
|
+
if (formProfile.value.cardNumber) {
|
|
249
|
+
const cleanNumber = formProfile.value.cardNumber.replace(/\s+/g, "");
|
|
305
250
|
if (cleanNumber) {
|
|
306
251
|
const cardInfo = validateCard(cleanNumber);
|
|
307
252
|
displayCardNumber.value = cardInfo.formatted;
|
|
@@ -311,8 +256,8 @@ const initializeCardNumberDisplay = () => {
|
|
|
311
256
|
|
|
312
257
|
// Format card number display on focus
|
|
313
258
|
const formatCardNumberDisplay = () => {
|
|
314
|
-
if (
|
|
315
|
-
const cleanNumber =
|
|
259
|
+
if (formProfile.value.cardNumber) {
|
|
260
|
+
const cleanNumber = formProfile.value.cardNumber.replace(/\s+/g, "");
|
|
316
261
|
if (cleanNumber) {
|
|
317
262
|
const cardInfo = validateCard(cleanNumber);
|
|
318
263
|
displayCardNumber.value = cardInfo.formatted;
|
|
@@ -322,7 +267,7 @@ const formatCardNumberDisplay = () => {
|
|
|
322
267
|
|
|
323
268
|
// Watch for changes in profile.cardNumber to sync with display
|
|
324
269
|
watch(
|
|
325
|
-
() =>
|
|
270
|
+
() => formProfile.value.cardNumber,
|
|
326
271
|
(newValue) => {
|
|
327
272
|
if (newValue) {
|
|
328
273
|
const cleanNumber = newValue.replace(/\s+/g, "");
|
|
@@ -345,39 +290,21 @@ onMounted(() => {
|
|
|
345
290
|
|
|
346
291
|
const generate = () => {
|
|
347
292
|
const fake = fakeId();
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
293
|
+
formProfile.value.city = fake.city;
|
|
294
|
+
formProfile.value.zipCode = fake?.zipCode;
|
|
295
|
+
formProfile.value.address = fake.address;
|
|
296
|
+
formProfile.value.state = fake.state;
|
|
297
|
+
formProfile.value.country = "US";
|
|
353
298
|
};
|
|
354
299
|
|
|
355
300
|
const chooseCountry = (f) => {
|
|
356
|
-
|
|
357
|
-
if (f !== "US")
|
|
301
|
+
formProfile.value.country = f;
|
|
302
|
+
if (f !== "US") formProfile.value.state = "";
|
|
358
303
|
};
|
|
359
304
|
|
|
360
305
|
const validate = (p) => {
|
|
361
|
-
errors.value = [];
|
|
362
306
|
if (!p.zipCode && !p.address && !p.state && !p.city) generate();
|
|
363
|
-
|
|
364
|
-
if (!p.zipCode) errors.value.push("zipCode");
|
|
365
|
-
if (!p.address) errors.value.push("address");
|
|
366
|
-
if (!p.state && p.country === "US") errors.value.push("state");
|
|
367
|
-
if (!p.city) errors.value.push("city");
|
|
368
|
-
if (!p.country) errors.value.push("country");
|
|
369
|
-
if (!/^\d{3,4}$/.test(`${p.cvv}`)) errors.value.push("cvv");
|
|
370
|
-
const cleanCardNumber = p.cardNumber.replace(/\s+/g, "");
|
|
371
|
-
// Validate card number based on type and length
|
|
372
|
-
const isValidCard =
|
|
373
|
-
cleanCardNumber.match(/^4\d{15}$/) || // Visa (16 digits)
|
|
374
|
-
cleanCardNumber.match(/^5[1-5]\d{14}$/) || // Mastercard (16 digits)
|
|
375
|
-
cleanCardNumber.match(/^3[47]\d{13}$/); // AMEX (15 digits)
|
|
376
|
-
if (!isValidCard) errors.value.push("cardNumber");
|
|
377
|
-
if (!p.expYear) errors.value.push("expYear");
|
|
378
|
-
if (!p.expMonth) errors.value.push("expMonth");
|
|
379
|
-
if (!p.expMonth) errors.value.push("expMonth");
|
|
380
|
-
return errors.value.length === 0;
|
|
307
|
+
return validateProfile(p);
|
|
381
308
|
};
|
|
382
309
|
|
|
383
310
|
const handleCreditCardUpdate = (event) => {
|
|
@@ -395,20 +322,20 @@ const handleCreditCardUpdate = (event) => {
|
|
|
395
322
|
const cardInfo = validateCard(subs);
|
|
396
323
|
|
|
397
324
|
// Store clean number (without spaces) in profile
|
|
398
|
-
|
|
325
|
+
formProfile.value.cardNumber = subs;
|
|
399
326
|
// Display formatted number in input
|
|
400
327
|
displayCardNumber.value = cardInfo.formatted;
|
|
401
328
|
};
|
|
402
329
|
|
|
403
330
|
function done() {
|
|
404
331
|
// Clear state if country is not US
|
|
405
|
-
if (
|
|
406
|
-
|
|
332
|
+
if (formProfile.value.country !== "US") {
|
|
333
|
+
formProfile.value.state = "";
|
|
407
334
|
}
|
|
408
335
|
|
|
409
|
-
ui.logger.Info("Created profile",
|
|
410
|
-
if (validate(
|
|
336
|
+
ui.logger.Info("Created profile", formProfile.value);
|
|
337
|
+
if (validate(formProfile.value) !== true) return;
|
|
411
338
|
ui.toggleModal("");
|
|
412
|
-
ui.addProfile(
|
|
339
|
+
ui.addProfile(formProfile.value);
|
|
413
340
|
}
|
|
414
341
|
</script>
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Row
|
|
3
|
-
class="relative grid-cols-
|
|
3
|
+
class="relative h-16 grid-cols-4 sm:grid-cols-5 md:grid-cols-7 lg:grid-cols-8 text-white"
|
|
4
4
|
@click="ui.setOpenContextMenu('')"
|
|
5
5
|
@click.right.prevent="ui.setOpenContextMenu('')"
|
|
6
6
|
@dblclick="handleDoubleClick"
|
|
7
7
|
@touchstart="handleTouchStart"
|
|
8
8
|
@touchend="handleTouchEnd">
|
|
9
|
-
<div class="col-span-3
|
|
9
|
+
<div class="col-span-2 sm:col-span-2 md:col-span-3 lg:col-span-2 flex">
|
|
10
10
|
<Checkbox
|
|
11
11
|
class="ml-0 mr-4"
|
|
12
12
|
:toggled="props.profile.selected"
|
|
13
13
|
@valueUpdate="ui.toggleProfileSelected(props.profile.id)" />
|
|
14
|
-
<h4 class="mx-auto text-white">
|
|
14
|
+
<h4 class="mx-auto text-center text-white">
|
|
15
15
|
{{ props.profile.profileName }}
|
|
16
16
|
</h4>
|
|
17
17
|
</div>
|
|
18
|
-
<div class="col-span-1 lg:col-span-2">
|
|
19
|
-
<h4 class="flex items-center justify-center gap-2 text-white">
|
|
18
|
+
<div class="col-span-1 hidden md:block lg:col-span-2">
|
|
19
|
+
<h4 class="flex items-center justify-center gap-2 text-center text-white">
|
|
20
20
|
<span class="hidden sm:block">
|
|
21
21
|
{{
|
|
22
22
|
props.profile.privacy
|
|
@@ -29,20 +29,20 @@
|
|
|
29
29
|
<img class="h-6 w-6" :src="getAccountType()" />
|
|
30
30
|
</h4>
|
|
31
31
|
</div>
|
|
32
|
-
<div class="col-span-1">
|
|
33
|
-
<h4 class="text-white">{{ expDate() }}</h4>
|
|
32
|
+
<div class="col-span-1 hidden md:block">
|
|
33
|
+
<h4 class="text-center text-white">{{ expDate() }}</h4>
|
|
34
34
|
</div>
|
|
35
35
|
<div class="col-span-1 flex justify-center">
|
|
36
36
|
<StatusBadge :enabled="props.profile.enabled" size="small" />
|
|
37
37
|
</div>
|
|
38
38
|
|
|
39
39
|
<div class="col-span-1 hidden lg:block">
|
|
40
|
-
<h4 class="flex justify-center gap-1 text-white">
|
|
40
|
+
<h4 class="flex justify-center gap-1 text-center text-white">
|
|
41
41
|
<TagLabel v-for="tag in props.profile.tags" :key="tag" :text="tag" />
|
|
42
42
|
</h4>
|
|
43
43
|
</div>
|
|
44
44
|
|
|
45
|
-
<div class="col-span-1 flex">
|
|
45
|
+
<div class="col-span-1 sm:col-span-1 flex">
|
|
46
46
|
<ActionButtonGroup>
|
|
47
47
|
<li>
|
|
48
48
|
<button @click="edit">
|
|
@@ -51,12 +51,12 @@
|
|
|
51
51
|
</li>
|
|
52
52
|
<li v-if="props.profile.enabled">
|
|
53
53
|
<button @click="disable">
|
|
54
|
-
<img class="
|
|
54
|
+
<img class="icon-md" src="/img/controls/disable.svg" />
|
|
55
55
|
</button>
|
|
56
56
|
</li>
|
|
57
57
|
<li v-else>
|
|
58
58
|
<button @click="enable">
|
|
59
|
-
<img class="
|
|
59
|
+
<img class="icon-md" src="/img/controls/enable.svg" />
|
|
60
60
|
</button>
|
|
61
61
|
</li>
|
|
62
62
|
<li>
|
|
@@ -66,26 +66,26 @@
|
|
|
66
66
|
</div>
|
|
67
67
|
</Row>
|
|
68
68
|
</template>
|
|
69
|
-
<style lang="scss" scoped>
|
|
70
|
-
h4 {
|
|
71
|
-
@apply text-center;
|
|
72
|
-
}
|
|
73
|
-
</style>
|
|
74
69
|
<script setup>
|
|
75
70
|
import { Row } from "@/components/Table";
|
|
76
|
-
import {
|
|
71
|
+
import { TrashIcon, EditIcon } from "@/components/icons";
|
|
77
72
|
import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
|
|
78
73
|
import StatusBadge from "@/components/ui/StatusBadge.vue";
|
|
79
74
|
import ActionButtonGroup from "@/components/ui/ActionButtonGroup.vue";
|
|
80
75
|
import { useUIStore } from "@/stores/ui";
|
|
81
|
-
import { validateCard } from "@/
|
|
76
|
+
import { validateCard } from "@/libs/utils/validation";
|
|
82
77
|
import TagLabel from "@/components/Editors/TagLabel.vue";
|
|
83
78
|
import { useRowSelection } from "@/composables/useRowSelection";
|
|
79
|
+
import { computed } from "vue";
|
|
80
|
+
import { useEnableDisable } from "@/composables/useEnableDisable";
|
|
84
81
|
|
|
85
82
|
const ui = useUIStore();
|
|
86
83
|
|
|
87
84
|
const props = defineProps({
|
|
88
|
-
profile: {
|
|
85
|
+
profile: {
|
|
86
|
+
type: Object,
|
|
87
|
+
required: true
|
|
88
|
+
}
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
const getAccountType = () => {
|
|
@@ -99,8 +99,7 @@ const getAccountType = () => {
|
|
|
99
99
|
|
|
100
100
|
const expDate = () =>
|
|
101
101
|
props.profile.privacy ? "••/••" : `${props.profile.expMonth}/${props.profile.expYear?.replace("20", "")}`;
|
|
102
|
-
const enable =
|
|
103
|
-
const disable = async () => await ui.addProfile({ ...props.profile, enabled: false });
|
|
102
|
+
const { enable, disable } = useEnableDisable(computed(() => props.profile), ui.addProfile);
|
|
104
103
|
const edit = () => {
|
|
105
104
|
ui.currentlyEditing = props.profile;
|
|
106
105
|
ui.toggleModal("create-profile");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
|
-
<div class="dropdown input-default
|
|
3
|
+
<div class="dropdown input-default h-10 w-12 md:h-10 flex justify-center items-center p-0 bg-dark-550 rounded-lg [background-clip:border-box]" ref="dropdownRef">
|
|
4
4
|
<span @click="toggleOpen" class="flex justify-between items-center z-50 text-white">
|
|
5
5
|
<div class="flex gap-3 justify-center">
|
|
6
6
|
<img class="w-5" :src="`/flags/${current?.toLowerCase()}.svg`" />
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<Teleport to="body">
|
|
10
10
|
<div
|
|
11
11
|
v-if="open && !disabled"
|
|
12
|
-
class="
|
|
12
|
+
class="min-w-20 bg-dark-400 border border-dark-650 rounded-lg shadow-2xl z-50 p-2 [max-height:192px] overflow-y-auto custom-scrollbar-y"
|
|
13
13
|
:style="menuStyle"
|
|
14
14
|
@click.stop
|
|
15
15
|
@wheel.stop
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
<div
|
|
18
18
|
v-for="(country, i) in countries"
|
|
19
19
|
v-bind:key="country"
|
|
20
|
-
:class="`cursor-pointer
|
|
20
|
+
:class="`px-3 py-2 text-sm text-white cursor-pointer rounded-md ${i === 0 ? '' : 'my-2'}`"
|
|
21
21
|
@click="set(country)">
|
|
22
|
-
<div class="flex
|
|
22
|
+
<div class="flex items-center justify-center gap-3 smooth-hover">
|
|
23
23
|
<span class="text-sm">{{ country }}</span>
|
|
24
24
|
<img class="w-5 ml-3" :src="`/flags/${country?.toLowerCase()}.svg`" />
|
|
25
25
|
</div>
|
|
@@ -40,9 +40,18 @@ const open = ref(false);
|
|
|
40
40
|
const dropdownRef = ref(null);
|
|
41
41
|
|
|
42
42
|
const props = defineProps({
|
|
43
|
-
value: {
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
value: {
|
|
44
|
+
type: String,
|
|
45
|
+
default: 'US'
|
|
46
|
+
},
|
|
47
|
+
onClick: {
|
|
48
|
+
type: Function,
|
|
49
|
+
required: true
|
|
50
|
+
},
|
|
51
|
+
disabled: {
|
|
52
|
+
type: Boolean,
|
|
53
|
+
default: false
|
|
54
|
+
}
|
|
46
55
|
});
|
|
47
56
|
|
|
48
57
|
const current = ref(props.value || "US");
|
|
@@ -82,56 +91,3 @@ watch(
|
|
|
82
91
|
(n) => (current.value = n)
|
|
83
92
|
);
|
|
84
93
|
</script>
|
|
85
|
-
|
|
86
|
-
<style scoped>
|
|
87
|
-
.special-dropdown {
|
|
88
|
-
@apply min-w-20;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.small-dropdown {
|
|
92
|
-
@apply h-10 !important;
|
|
93
|
-
background-clip: border-box !important;
|
|
94
|
-
/* border-radius: 100% !important; */
|
|
95
|
-
padding: 0;
|
|
96
|
-
width: 3em !important;
|
|
97
|
-
display: flex;
|
|
98
|
-
justify-items: center;
|
|
99
|
-
justify-content: center;
|
|
100
|
-
}
|
|
101
|
-
|
|
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;
|
|
136
|
-
}
|
|
137
|
-
</style>
|