@necrolab/dashboard 0.4.32 → 0.4.34
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 +4 -1
- package/package.json +1 -1
- package/src/App.vue +0 -8
- package/src/assets/css/_input.scss +1 -1
- package/src/assets/css/main.scss +52 -5
- package/src/components/Editors/Account/Account.vue +101 -8
- package/src/components/Editors/Account/AccountView.vue +1 -0
- package/src/components/Editors/Account/CreateAccount.vue +10 -4
- package/src/components/Editors/Profile/CreateProfile.vue +134 -40
- package/src/components/Editors/Profile/Profile.vue +101 -8
- package/src/components/Editors/Profile/ProfileView.vue +1 -0
- package/src/components/Filter/Filter.vue +15 -3
- package/src/components/Tasks/Stats.vue +6 -6
- package/src/components/Tasks/Task.vue +140 -24
- package/src/components/Tasks/TaskView.vue +68 -21
- package/src/components/Tasks/Utilities.vue +4 -5
- package/src/components/ui/Modal.vue +12 -1
- package/src/components/ui/controls/atomic/Checkbox.vue +119 -9
- package/src/components/ui/controls/atomic/Dropdown.vue +8 -2
- package/src/components/ui/controls/atomic/MultiDropdown.vue +2 -1
- package/src/stores/sampleData.js +45 -79
- package/src/utils/debug.js +1 -1
- package/src/views/Console.vue +16 -9
- package/src/views/FilterBuilder.vue +43 -21
- package/src/views/Tasks.vue +19 -73
package/package.json
CHANGED
package/src/App.vue
CHANGED
|
@@ -264,14 +264,6 @@ watch(
|
|
|
264
264
|
const layout = computed(() => router.currentRoute.value.meta.layout);
|
|
265
265
|
</script>
|
|
266
266
|
<style lang="scss">
|
|
267
|
-
.task-buttons {
|
|
268
|
-
@apply flex mx-auto gap-x-4;
|
|
269
|
-
|
|
270
|
-
svg {
|
|
271
|
-
width: 15px;
|
|
272
|
-
height: 15px;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
267
|
|
|
276
268
|
.dropdown {
|
|
277
269
|
position: relative;
|
package/src/assets/css/main.scss
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
@use "input";
|
|
2
2
|
|
|
3
|
+
/* Global input styling fixes for iOS and other platforms */
|
|
4
|
+
input, textarea, select, button {
|
|
5
|
+
/* Remove iOS blue highlight/border */
|
|
6
|
+
-webkit-tap-highlight-color: transparent;
|
|
7
|
+
-webkit-touch-callout: none;
|
|
8
|
+
|
|
9
|
+
/* Remove default focus outline */
|
|
10
|
+
outline: none;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
input, textarea, select {
|
|
14
|
+
/* Remove browser default styling */
|
|
15
|
+
-webkit-appearance: none;
|
|
16
|
+
-moz-appearance: none;
|
|
17
|
+
appearance: none;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Enhanced focus state removal for iOS */
|
|
21
|
+
input:focus, textarea:focus, select:focus {
|
|
22
|
+
outline: none !important;
|
|
23
|
+
-webkit-tap-highlight-color: transparent !important;
|
|
24
|
+
box-shadow: none !important;
|
|
25
|
+
}
|
|
26
|
+
|
|
3
27
|
// Base styles
|
|
4
28
|
html {
|
|
5
29
|
overscroll-behavior: contain;
|
|
@@ -88,16 +112,42 @@ img {
|
|
|
88
112
|
|
|
89
113
|
svg {
|
|
90
114
|
@apply ml-2;
|
|
115
|
+
color: #6e7084 !important;
|
|
116
|
+
width: 16px;
|
|
117
|
+
height: 16px;
|
|
118
|
+
fill: #6e7084 !important;
|
|
91
119
|
}
|
|
92
120
|
}
|
|
93
121
|
|
|
94
122
|
// Use CSS custom property to avoid global path selector
|
|
95
123
|
.label-override svg {
|
|
96
|
-
color: #6e7084;
|
|
124
|
+
color: #6e7084 !important;
|
|
125
|
+
fill: #6e7084 !important;
|
|
97
126
|
}
|
|
98
127
|
|
|
99
128
|
.label-override svg path {
|
|
100
|
-
fill:
|
|
129
|
+
fill: #6e7084 !important;
|
|
130
|
+
color: #6e7084 !important;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.label-override svg * {
|
|
134
|
+
fill: #6e7084 !important;
|
|
135
|
+
color: #6e7084 !important;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.switch-wrapper svg {
|
|
139
|
+
color: #6e7084 !important;
|
|
140
|
+
fill: #6e7084 !important;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.switch-wrapper svg path {
|
|
144
|
+
fill: #6e7084 !important;
|
|
145
|
+
color: #6e7084 !important;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.switch-wrapper svg * {
|
|
149
|
+
fill: #6e7084 !important;
|
|
150
|
+
color: #6e7084 !important;
|
|
101
151
|
}
|
|
102
152
|
|
|
103
153
|
// Component utilities
|
|
@@ -109,9 +159,6 @@ img {
|
|
|
109
159
|
@apply w-2 h-2 rounded-full;
|
|
110
160
|
}
|
|
111
161
|
|
|
112
|
-
.task-buttons {
|
|
113
|
-
@apply bg-dark-600 px-1 lg:px-2 rounded-full shadow-lg items-center flex gap-x-1;
|
|
114
|
-
}
|
|
115
162
|
|
|
116
163
|
.mobile-icons {
|
|
117
164
|
@apply flex lg:hidden ml-auto items-center gap-x-2;
|
|
@@ -60,28 +60,76 @@ h4 {
|
|
|
60
60
|
@apply text-center;
|
|
61
61
|
}
|
|
62
62
|
.task-buttons {
|
|
63
|
-
@apply flex
|
|
64
|
-
|
|
63
|
+
@apply flex items-center justify-center mx-auto;
|
|
64
|
+
background: linear-gradient(135deg, rgba(45, 47, 74, 0.8), rgba(32, 32, 54, 0.9));
|
|
65
|
+
border: 1px solid rgba(74, 74, 97, 0.3);
|
|
66
|
+
border-radius: 10px;
|
|
67
|
+
padding: 2px;
|
|
68
|
+
gap: 1px;
|
|
69
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
|
70
|
+
backdrop-filter: blur(8px);
|
|
71
|
+
|
|
65
72
|
button {
|
|
66
|
-
@apply
|
|
73
|
+
@apply flex items-center justify-center text-white transition-all duration-150 border-0 outline-0 relative;
|
|
74
|
+
background: transparent;
|
|
75
|
+
border-radius: 7px;
|
|
76
|
+
width: 28px;
|
|
77
|
+
height: 28px;
|
|
78
|
+
position: relative;
|
|
79
|
+
|
|
80
|
+
&:hover {
|
|
81
|
+
background: rgba(74, 74, 97, 0.3);
|
|
82
|
+
transform: scale(1.05);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
&:active {
|
|
86
|
+
background: rgba(74, 74, 97, 0.5);
|
|
87
|
+
transform: scale(0.95);
|
|
88
|
+
}
|
|
67
89
|
}
|
|
68
|
-
|
|
90
|
+
|
|
69
91
|
svg {
|
|
70
|
-
|
|
92
|
+
width: 14px;
|
|
93
|
+
height: 14px;
|
|
94
|
+
position: relative;
|
|
95
|
+
z-index: 1;
|
|
96
|
+
|
|
97
|
+
path {
|
|
98
|
+
fill: currentColor;
|
|
99
|
+
}
|
|
71
100
|
}
|
|
72
|
-
|
|
101
|
+
|
|
73
102
|
img {
|
|
74
|
-
|
|
103
|
+
width: 14px;
|
|
104
|
+
height: 14px;
|
|
105
|
+
position: relative;
|
|
106
|
+
z-index: 1;
|
|
75
107
|
}
|
|
76
108
|
}
|
|
77
109
|
|
|
110
|
+
// Tablet optimization
|
|
78
111
|
@media (max-width: 1024px) {
|
|
79
112
|
h4 {
|
|
80
113
|
font-size: 10px !important;
|
|
81
114
|
}
|
|
115
|
+
|
|
82
116
|
.task-buttons {
|
|
83
|
-
|
|
117
|
+
padding: 2px;
|
|
118
|
+
gap: 1px;
|
|
119
|
+
border-radius: 8px;
|
|
120
|
+
|
|
121
|
+
button {
|
|
122
|
+
width: 26px;
|
|
123
|
+
height: 26px;
|
|
124
|
+
border-radius: 6px;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
svg, img {
|
|
128
|
+
width: 13px;
|
|
129
|
+
height: 13px;
|
|
130
|
+
}
|
|
84
131
|
}
|
|
132
|
+
|
|
85
133
|
.task-id {
|
|
86
134
|
font-size: 6px !important;
|
|
87
135
|
margin-right: -12px;
|
|
@@ -91,6 +139,51 @@ h4 {
|
|
|
91
139
|
font-size: 7px !important;
|
|
92
140
|
}
|
|
93
141
|
}
|
|
142
|
+
|
|
143
|
+
// Mobile optimization
|
|
144
|
+
@media (max-width: 768px) {
|
|
145
|
+
.task-buttons {
|
|
146
|
+
padding: 1px;
|
|
147
|
+
gap: 0;
|
|
148
|
+
border-radius: 7px;
|
|
149
|
+
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
|
150
|
+
|
|
151
|
+
button {
|
|
152
|
+
width: 22px;
|
|
153
|
+
height: 22px;
|
|
154
|
+
border-radius: 5px;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
svg, img {
|
|
158
|
+
width: 11px;
|
|
159
|
+
height: 11px;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// iPhone vertical (portrait) specific
|
|
165
|
+
@media (max-width: 480px) and (orientation: portrait) {
|
|
166
|
+
.task-buttons {
|
|
167
|
+
padding: 1px;
|
|
168
|
+
gap: 0;
|
|
169
|
+
border-radius: 6px;
|
|
170
|
+
|
|
171
|
+
button {
|
|
172
|
+
width: 18px;
|
|
173
|
+
height: 18px;
|
|
174
|
+
border-radius: 4px;
|
|
175
|
+
|
|
176
|
+
&:hover {
|
|
177
|
+
transform: scale(1.1);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
svg, img {
|
|
182
|
+
width: 9px;
|
|
183
|
+
height: 9px;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
94
187
|
</style>
|
|
95
188
|
<script setup>
|
|
96
189
|
import { Row } from "@/components/Table";
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
<div>
|
|
10
10
|
<div class="my-3 grid grid-cols-12 gap-3 mt-7 mb-4">
|
|
11
11
|
<div class="input-wrapper col-span-4 z-10">
|
|
12
|
-
<label class="label-override">Profile Tag
|
|
12
|
+
<label class="label-override">Profile Tag
|
|
13
|
+
<TagIcon />
|
|
14
|
+
</label>
|
|
13
15
|
<Dropdown
|
|
14
16
|
:class="`input-default dropdown w-full ${errors.includes('profileTag') ? 'error' : ''}`"
|
|
15
17
|
:default="ui.profile.accountTags[0]"
|
|
@@ -19,7 +21,9 @@
|
|
|
19
21
|
/>
|
|
20
22
|
</div>
|
|
21
23
|
<div class="input-wrapper col-span-8">
|
|
22
|
-
<label class="label-override">Email
|
|
24
|
+
<label class="label-override">Email
|
|
25
|
+
<MailIcon />
|
|
26
|
+
</label>
|
|
23
27
|
<div :class="`input-default required ${errors.includes('email') ? 'error' : ''}`">
|
|
24
28
|
<input
|
|
25
29
|
placeholder="email@example.com"
|
|
@@ -39,7 +43,9 @@
|
|
|
39
43
|
</div>
|
|
40
44
|
</div>
|
|
41
45
|
<div class="input-wrapper col-span-12">
|
|
42
|
-
<label class="label-override">Password
|
|
46
|
+
<label class="label-override">Password
|
|
47
|
+
<KeyIcon />
|
|
48
|
+
</label>
|
|
43
49
|
<div :class="`input-default required ${errors.includes('password') ? 'error' : ''}`">
|
|
44
50
|
<input
|
|
45
51
|
placeholder="***********"
|
|
@@ -72,7 +78,7 @@
|
|
|
72
78
|
</style>
|
|
73
79
|
<script setup>
|
|
74
80
|
import Modal from "@/components/ui/Modal.vue";
|
|
75
|
-
import { EditIcon } from "@/components/icons";
|
|
81
|
+
import { EditIcon, MailIcon, KeyIcon, ProfileIcon, TimerIcon, SandclockIcon, TagIcon } from "@/components/icons";
|
|
76
82
|
import { useUIStore } from "@/stores/ui";
|
|
77
83
|
import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
|
|
78
84
|
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
<div class="grid grid-cols-12 gap-3 my-3">
|
|
11
11
|
<!-- Profile tag -->
|
|
12
12
|
<div class="input-wrapper col-span-4" style="z-index: 30 !important">
|
|
13
|
-
<label class="label-override mb-2">Profile Tag
|
|
13
|
+
<label class="label-override mb-2">Profile Tag
|
|
14
|
+
<TagIcon />
|
|
15
|
+
</label>
|
|
14
16
|
<Dropdown
|
|
15
17
|
:class="`input-default dropdown w-full ${errors.includes('profileTag') ? 'error' : ''}`"
|
|
16
18
|
:default="ui.profile.accountTags[0]"
|
|
@@ -22,7 +24,9 @@
|
|
|
22
24
|
|
|
23
25
|
<!-- Card Number -->
|
|
24
26
|
<div class="input-wrapper col-span-8 z-0">
|
|
25
|
-
<label class="label-override mb-2">Card Number
|
|
27
|
+
<label class="label-override mb-2">Card Number
|
|
28
|
+
<CartIcon />
|
|
29
|
+
</label>
|
|
26
30
|
<div :class="`input-default ${errors.includes('cardNumber') ? 'error' : ''}`">
|
|
27
31
|
<input
|
|
28
32
|
ref="cardNumberInput"
|
|
@@ -38,7 +42,9 @@
|
|
|
38
42
|
|
|
39
43
|
<!-- Country chooser -->
|
|
40
44
|
<div class="input-wrapper col-span-2">
|
|
41
|
-
<label class="label-override mb-2">Country
|
|
45
|
+
<label class="label-override mb-2">Country
|
|
46
|
+
<SandclockIcon />
|
|
47
|
+
</label>
|
|
42
48
|
<ProfileCountryChooser
|
|
43
49
|
class="h-8"
|
|
44
50
|
:value="profile.country"
|
|
@@ -49,7 +55,9 @@
|
|
|
49
55
|
|
|
50
56
|
<!-- Exp Year -->
|
|
51
57
|
<div class="input-wrapper col-span-5">
|
|
52
|
-
<label class="label-override mb-2 z-0">Expiry Year
|
|
58
|
+
<label class="label-override mb-2 z-0">Expiry Year
|
|
59
|
+
<TimerIcon />
|
|
60
|
+
</label>
|
|
53
61
|
<Dropdown
|
|
54
62
|
:class="`input-default dropdown w-full ${errors.includes('expYear') ? 'error' : ''}`"
|
|
55
63
|
default="Expiry Year"
|
|
@@ -66,8 +74,10 @@
|
|
|
66
74
|
</div>
|
|
67
75
|
|
|
68
76
|
<!-- Exp Month -->
|
|
69
|
-
<div class="input-wrapper col-span-5">
|
|
70
|
-
<label class="label-override mb-2">Expiry Month
|
|
77
|
+
<div class="input-wrapper col-span-5 z-2">
|
|
78
|
+
<label class="label-override mb-2">Expiry Month
|
|
79
|
+
<TimerIcon />
|
|
80
|
+
</label>
|
|
71
81
|
<Dropdown
|
|
72
82
|
:class="`input-default dropdown w-full ${errors.includes('expMonth') ? 'error' : ''}`"
|
|
73
83
|
default="Expiry Month"
|
|
@@ -79,7 +89,9 @@
|
|
|
79
89
|
|
|
80
90
|
<!-- CVV -->
|
|
81
91
|
<div class="input-wrapper col-span-6 md:col-span-4 z-0">
|
|
82
|
-
<label class="label-override mb-2">CVV
|
|
92
|
+
<label class="label-override mb-2">CVV
|
|
93
|
+
<ShieldIcon />
|
|
94
|
+
</label>
|
|
83
95
|
<div :class="`input-default ${errors.includes('cvv') ? 'error' : ''}`">
|
|
84
96
|
<input
|
|
85
97
|
placeholder="183"
|
|
@@ -94,24 +106,28 @@
|
|
|
94
106
|
|
|
95
107
|
<!-- City -->
|
|
96
108
|
<div class="input-wrapper col-span-6 md:col-span-4 z-0">
|
|
97
|
-
<label class="label-override mb-2">City
|
|
109
|
+
<label class="label-override mb-2">City
|
|
110
|
+
<StadiumIcon />
|
|
111
|
+
</label>
|
|
98
112
|
<div :class="`input-default ${errors.includes('city') ? 'error' : ''}`">
|
|
99
113
|
<input placeholder="Denver" v-model="profile.city" />
|
|
100
114
|
</div>
|
|
101
115
|
</div>
|
|
102
116
|
|
|
103
117
|
<!-- State -->
|
|
104
|
-
<div class="input-wrapper col-span-6 md:col-span-4 z-
|
|
105
|
-
<label class="label-override mb-2">State
|
|
118
|
+
<div class="input-wrapper col-span-6 md:col-span-4 z-1">
|
|
119
|
+
<label class="label-override mb-2">State
|
|
120
|
+
<SandclockIcon />
|
|
121
|
+
</label>
|
|
106
122
|
<div v-if="profile.country === 'US'" :class="`${errors.includes('state') ? 'error' : ''}`">
|
|
107
123
|
<Dropdown
|
|
108
124
|
class="input-default w-full"
|
|
109
125
|
default="Select State"
|
|
110
|
-
:onClick="(state) => profile.state = state"
|
|
126
|
+
:onClick="(state) => (profile.state = state)"
|
|
111
127
|
:options="usStates"
|
|
112
128
|
:allowDefault="false"
|
|
113
129
|
rightAmount="right-2"
|
|
114
|
-
:
|
|
130
|
+
:value="profile.state"
|
|
115
131
|
/>
|
|
116
132
|
</div>
|
|
117
133
|
<div v-else :class="`input-default ${errors.includes('state') ? 'error' : ''}`">
|
|
@@ -121,26 +137,45 @@
|
|
|
121
137
|
|
|
122
138
|
<!-- Zip -->
|
|
123
139
|
<div class="input-wrapper col-span-6 md:col-span-4 z-0">
|
|
124
|
-
<label class="label-override mb-2">Zip
|
|
140
|
+
<label class="label-override mb-2">Zip
|
|
141
|
+
<KeyIcon />
|
|
142
|
+
</label>
|
|
125
143
|
<div :class="`input-default ${errors.includes('zipCode') ? 'error' : ''}`">
|
|
126
|
-
<input placeholder="
|
|
144
|
+
<input placeholder="10005" type="number" min="1" max="12" v-model="profile.zipCode" />
|
|
127
145
|
</div>
|
|
128
146
|
</div>
|
|
129
147
|
|
|
130
148
|
<!-- Address -->
|
|
131
149
|
<div class="input-wrapper col-span-6 md:col-span-4 z-0">
|
|
132
|
-
<label class="label-override mb-2">Address
|
|
150
|
+
<label class="label-override mb-2">Address
|
|
151
|
+
<HandIcon />
|
|
152
|
+
</label>
|
|
133
153
|
<div :class="`input-default ${errors.includes('address') ? 'error' : ''}`">
|
|
134
|
-
<input placeholder="
|
|
154
|
+
<input placeholder="100 5th Avenue" v-model="profile.address" />
|
|
135
155
|
</div>
|
|
136
156
|
</div>
|
|
137
157
|
|
|
138
158
|
<!-- Generate -->
|
|
139
159
|
<div class="input-wrapper col-span-6 md:col-span-4 z-0">
|
|
140
|
-
<label class="label-override mb-2
|
|
160
|
+
<label class="label-override mb-2"> Fake ID
|
|
161
|
+
<WildcardIcon />
|
|
162
|
+
</label>
|
|
141
163
|
<div class="input-default mt-2 w-full h-10 flex items-center">
|
|
142
|
-
<button
|
|
143
|
-
|
|
164
|
+
<button
|
|
165
|
+
@click="generate"
|
|
166
|
+
class="text-white w-full text-xs flex items-center justify-center gap-2"
|
|
167
|
+
>
|
|
168
|
+
<svg
|
|
169
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
170
|
+
width="14"
|
|
171
|
+
height="14"
|
|
172
|
+
viewBox="0 0 24 24"
|
|
173
|
+
fill="none"
|
|
174
|
+
stroke="currentColor"
|
|
175
|
+
stroke-width="2"
|
|
176
|
+
stroke-linecap="round"
|
|
177
|
+
stroke-linejoin="round"
|
|
178
|
+
>
|
|
144
179
|
<rect x="3" y="4" width="18" height="16" rx="2" />
|
|
145
180
|
<line x1="7" y1="2" x2="7" y2="6" />
|
|
146
181
|
<line x1="17" y1="2" x2="17" y2="6" />
|
|
@@ -169,6 +204,12 @@
|
|
|
169
204
|
.z-0 {
|
|
170
205
|
z-index: 0 !important;
|
|
171
206
|
}
|
|
207
|
+
.z-1 {
|
|
208
|
+
z-index: 1 !important;
|
|
209
|
+
}
|
|
210
|
+
.z-1 {
|
|
211
|
+
z-index: 2 !important;
|
|
212
|
+
}
|
|
172
213
|
|
|
173
214
|
.error {
|
|
174
215
|
border-width: 2px !important;
|
|
@@ -177,6 +218,7 @@
|
|
|
177
218
|
</style>
|
|
178
219
|
<script setup>
|
|
179
220
|
import Modal from "@/components/ui/Modal.vue";
|
|
221
|
+
import { MailIcon, CartIcon, ShieldIcon, StadiumIcon, KeyIcon, HandIcon, ProfileIcon, SandclockIcon, TimerIcon, TagIcon, WildcardIcon } from "@/components/icons";
|
|
180
222
|
import { EditIcon } from "@/components/icons";
|
|
181
223
|
import ProfileCountryChooser from "@/components/Editors/Profile/ProfileCountryChooser.vue";
|
|
182
224
|
import { useUIStore } from "@/stores/ui";
|
|
@@ -191,7 +233,55 @@ const ui = useUIStore();
|
|
|
191
233
|
|
|
192
234
|
// US States list (excluding GA and PR, alphabetically ordered)
|
|
193
235
|
const usStates = [
|
|
194
|
-
"AK",
|
|
236
|
+
"AK",
|
|
237
|
+
"AL",
|
|
238
|
+
"AR",
|
|
239
|
+
"AZ",
|
|
240
|
+
"CA",
|
|
241
|
+
"CO",
|
|
242
|
+
"CT",
|
|
243
|
+
"DE",
|
|
244
|
+
"FL",
|
|
245
|
+
"HI",
|
|
246
|
+
"IA",
|
|
247
|
+
"ID",
|
|
248
|
+
"IL",
|
|
249
|
+
"IN",
|
|
250
|
+
"KS",
|
|
251
|
+
"KY",
|
|
252
|
+
"LA",
|
|
253
|
+
"MA",
|
|
254
|
+
"MD",
|
|
255
|
+
"ME",
|
|
256
|
+
"MI",
|
|
257
|
+
"MN",
|
|
258
|
+
"MO",
|
|
259
|
+
"MS",
|
|
260
|
+
"MT",
|
|
261
|
+
"NC",
|
|
262
|
+
"ND",
|
|
263
|
+
"NE",
|
|
264
|
+
"NH",
|
|
265
|
+
"NJ",
|
|
266
|
+
"NM",
|
|
267
|
+
"NV",
|
|
268
|
+
"NY",
|
|
269
|
+
"OH",
|
|
270
|
+
"OK",
|
|
271
|
+
"OR",
|
|
272
|
+
"PA",
|
|
273
|
+
"RI",
|
|
274
|
+
"SC",
|
|
275
|
+
"SD",
|
|
276
|
+
"TN",
|
|
277
|
+
"TX",
|
|
278
|
+
"UT",
|
|
279
|
+
"VA",
|
|
280
|
+
"VT",
|
|
281
|
+
"WA",
|
|
282
|
+
"WI",
|
|
283
|
+
"WV",
|
|
284
|
+
"WY"
|
|
195
285
|
];
|
|
196
286
|
const cardNumberInput = ref(null);
|
|
197
287
|
const profile = ref({
|
|
@@ -232,17 +322,20 @@ const formatCardNumberDisplay = () => {
|
|
|
232
322
|
};
|
|
233
323
|
|
|
234
324
|
// Watch for changes in profile.cardNumber to sync with display
|
|
235
|
-
watch(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (
|
|
239
|
-
const
|
|
240
|
-
|
|
325
|
+
watch(
|
|
326
|
+
() => profile.value.cardNumber,
|
|
327
|
+
(newValue) => {
|
|
328
|
+
if (newValue) {
|
|
329
|
+
const cleanNumber = newValue.replace(/\s+/g, "");
|
|
330
|
+
if (cleanNumber) {
|
|
331
|
+
const cardInfo = validateCard(cleanNumber);
|
|
332
|
+
displayCardNumber.value = cardInfo.formatted;
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
displayCardNumber.value = "";
|
|
241
336
|
}
|
|
242
|
-
} else {
|
|
243
|
-
displayCardNumber.value = "";
|
|
244
337
|
}
|
|
245
|
-
|
|
338
|
+
);
|
|
246
339
|
|
|
247
340
|
// Initialize on modal open
|
|
248
341
|
onMounted(() => {
|
|
@@ -277,9 +370,10 @@ const validate = (p) => {
|
|
|
277
370
|
if (!/^\d{3,4}$/.test(`${p.cvv}`)) errors.value.push("cvv");
|
|
278
371
|
const cleanCardNumber = p.cardNumber.replace(/\s+/g, "");
|
|
279
372
|
// Validate card number based on type and length
|
|
280
|
-
const isValidCard =
|
|
281
|
-
|
|
282
|
-
|
|
373
|
+
const isValidCard =
|
|
374
|
+
cleanCardNumber.match(/^4\d{15}$/) || // Visa (16 digits)
|
|
375
|
+
cleanCardNumber.match(/^5[1-5]\d{14}$/) || // Mastercard (16 digits)
|
|
376
|
+
cleanCardNumber.match(/^3[47]\d{13}$/); // AMEX (15 digits)
|
|
283
377
|
if (!isValidCard) errors.value.push("cardNumber");
|
|
284
378
|
if (!p.expYear) errors.value.push("expYear");
|
|
285
379
|
if (!p.expMonth) errors.value.push("expMonth");
|
|
@@ -289,18 +383,18 @@ const validate = (p) => {
|
|
|
289
383
|
|
|
290
384
|
const handleCreditCardUpdate = (event) => {
|
|
291
385
|
const value = event.target.value.replace(/\D/g, "");
|
|
292
|
-
|
|
386
|
+
|
|
293
387
|
// Determine max length based on card type
|
|
294
388
|
let maxLength = 16; // Default for Visa/Mastercard
|
|
295
|
-
if (value.startsWith(
|
|
389
|
+
if (value.startsWith("3")) {
|
|
296
390
|
maxLength = 15; // AMEX
|
|
297
|
-
} else if (value.startsWith(
|
|
391
|
+
} else if (value.startsWith("4") || value.startsWith("5")) {
|
|
298
392
|
maxLength = 16; // Visa/Mastercard
|
|
299
393
|
}
|
|
300
|
-
|
|
394
|
+
|
|
301
395
|
const subs = value.substring(0, maxLength);
|
|
302
396
|
const cardInfo = validateCard(subs);
|
|
303
|
-
|
|
397
|
+
|
|
304
398
|
// Store clean number (without spaces) in profile
|
|
305
399
|
profile.value.cardNumber = subs;
|
|
306
400
|
// Display formatted number in input
|
|
@@ -309,10 +403,10 @@ const handleCreditCardUpdate = (event) => {
|
|
|
309
403
|
|
|
310
404
|
function done() {
|
|
311
405
|
// Clear state if country is not US
|
|
312
|
-
if (profile.value.country !==
|
|
313
|
-
profile.value.state =
|
|
406
|
+
if (profile.value.country !== "US") {
|
|
407
|
+
profile.value.state = "";
|
|
314
408
|
}
|
|
315
|
-
|
|
409
|
+
|
|
316
410
|
ui.logger.Info("Created profile", profile.value);
|
|
317
411
|
if (validate(profile.value) !== true) return;
|
|
318
412
|
ui.toggleModal("");
|