@necrolab/dashboard 0.4.38 → 0.4.40
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 +7 -1
- package/.prettierrc +14 -1
- package/backend/api.js +25 -16
- package/backend/auth.js +2 -2
- package/backend/batching.js +1 -1
- package/backend/endpoints.js +5 -5
- package/backend/index.js +2 -2
- package/backend/mock-data.js +27 -28
- package/backend/mock-src/classes/logger.js +5 -7
- package/backend/mock-src/classes/utils.js +3 -2
- package/backend/mock-src/ticketmaster.js +2 -2
- package/backend/validator.js +2 -2
- package/dev-server.js +136 -0
- package/index.html +1 -1
- package/index.js +1 -1
- package/package.json +8 -6
- package/postcss.config.js +1 -1
- package/postinstall.js +30 -16
- 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/manifest.json +4 -4
- package/src/App.vue +471 -49
- package/src/assets/css/_input.scss +37 -37
- package/src/assets/css/main.scss +177 -30
- package/src/assets/img/logo_icon-old.png +0 -0
- package/src/assets/img/logo_icon.png +0 -0
- package/src/components/Auth/LoginForm.vue +12 -5
- package/src/components/Editors/Account/Account.vue +19 -19
- package/src/components/Editors/Account/AccountCreator.vue +53 -24
- package/src/components/Editors/Account/AccountView.vue +79 -17
- package/src/components/Editors/Account/CreateAccount.vue +47 -28
- package/src/components/Editors/Profile/Profile.vue +24 -24
- package/src/components/Editors/Profile/ProfileView.vue +67 -16
- package/src/components/Editors/TagLabel.vue +6 -7
- package/src/components/Filter/FilterPreview.vue +0 -4
- package/src/components/Table/Table.vue +15 -0
- package/src/components/Tasks/Controls/DesktopControls.vue +1 -1
- package/src/components/Tasks/CreateTaskAXS.vue +15 -15
- package/src/components/Tasks/CreateTaskTM.vue +5 -4
- package/src/components/Tasks/Stats.vue +22 -16
- package/src/components/Tasks/Task.vue +100 -81
- package/src/components/Tasks/TaskView.vue +25 -23
- package/src/components/Tasks/Utilities.vue +1 -1
- package/src/components/icons/Mail.vue +2 -2
- package/src/components/ui/Modal.vue +84 -15
- package/src/components/ui/Navbar.vue +118 -39
- package/src/components/ui/controls/atomic/Dropdown.vue +23 -3
- package/src/components/ui/controls/atomic/MultiDropdown.vue +43 -23
- package/src/stores/sampleData.js +89 -64
- package/src/stores/ui.js +30 -4
- package/src/views/Accounts.vue +2 -2
- package/src/views/Console.vue +276 -41
- package/src/views/Editor.vue +175 -28
- package/src/views/FilterBuilder.vue +45 -49
- package/src/views/Login.vue +134 -12
- package/src/views/Profiles.vue +8 -8
- package/src/views/Tasks.vue +51 -2
- package/tailwind.config.js +2 -2
- package/vite.config.js +34 -1
- package/vue.config.js +1 -1
- package/{workbox-config.js → workbox-config.cjs} +1 -4
|
@@ -7,20 +7,20 @@
|
|
|
7
7
|
<div class="col-span-3 lg:col-span-2 flex">
|
|
8
8
|
<Checkbox
|
|
9
9
|
class="ml-0 mr-4"
|
|
10
|
-
:toggled="props.
|
|
11
|
-
@valueUpdate="ui.toggleAccountSelected(props.
|
|
10
|
+
:toggled="props.account.selected"
|
|
11
|
+
@valueUpdate="ui.toggleAccountSelected(props.account._id)"
|
|
12
12
|
/>
|
|
13
|
-
<h4 class="mx-auto text-white" @click="copy(props.
|
|
14
|
-
{{ props.
|
|
13
|
+
<h4 class="mx-auto text-white" @click="copy(props.account.email)">
|
|
14
|
+
{{ props.account.email }}
|
|
15
15
|
</h4>
|
|
16
16
|
</div>
|
|
17
|
-
<div class="col-span-2 hidden ipadlg:block" @click="copy(props.
|
|
17
|
+
<div class="col-span-2 hidden ipadlg:block" @click="copy(props.account.password)">
|
|
18
18
|
<h4 class="text-white">
|
|
19
|
-
{{ props.
|
|
19
|
+
{{ props.account.privacy ? "•".repeat(props.account.password.length) : props.account.password }}
|
|
20
20
|
</h4>
|
|
21
21
|
</div>
|
|
22
22
|
<div class="col-span-1">
|
|
23
|
-
<h4 v-if="props.
|
|
23
|
+
<h4 v-if="props.account.enabled" class="text-green-400 flex justify-center">
|
|
24
24
|
<img class="w-3 h-3 green" src="/img/controls/enable.svg" />
|
|
25
25
|
</h4>
|
|
26
26
|
<h4 v-else class="text-red-400 flex justify-center">
|
|
@@ -30,18 +30,18 @@
|
|
|
30
30
|
|
|
31
31
|
<div class="col-span-1 hidden lg:block">
|
|
32
32
|
<h4 class="text-white flex justify-center gap-1">
|
|
33
|
-
<TagLabel v-for="tag in props.
|
|
33
|
+
<TagLabel v-for="tag in props.account.tags" :key="tag" :text="tag" />
|
|
34
34
|
</h4>
|
|
35
35
|
</div>
|
|
36
36
|
|
|
37
37
|
<div class="col-span-1 flex">
|
|
38
|
-
<ul class="
|
|
38
|
+
<ul class="account-buttons">
|
|
39
39
|
<li>
|
|
40
40
|
<button @click="edit">
|
|
41
41
|
<EditIcon />
|
|
42
42
|
</button>
|
|
43
43
|
</li>
|
|
44
|
-
<li v-if="props.
|
|
44
|
+
<li v-if="props.account.enabled">
|
|
45
45
|
<button @click="disable">
|
|
46
46
|
<img class="w-4 h-4" src="/img/controls/disable.svg" />
|
|
47
47
|
</button>
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
h4 {
|
|
60
60
|
@apply text-center;
|
|
61
61
|
}
|
|
62
|
-
.
|
|
62
|
+
.account-buttons {
|
|
63
63
|
@apply flex items-center justify-center mx-auto bg-dark-500 border border-dark-650 rounded;
|
|
64
64
|
padding: 3px;
|
|
65
65
|
gap: 2px;
|
|
@@ -102,7 +102,7 @@ h4 {
|
|
|
102
102
|
font-size: 10px !important;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
.
|
|
105
|
+
.account-buttons {
|
|
106
106
|
padding: 3px;
|
|
107
107
|
gap: 2px;
|
|
108
108
|
|
|
@@ -118,7 +118,7 @@ h4 {
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
.
|
|
121
|
+
.account-id {
|
|
122
122
|
font-size: 6px !important;
|
|
123
123
|
margin-right: -12px;
|
|
124
124
|
margin-top: 20px;
|
|
@@ -127,7 +127,7 @@ h4 {
|
|
|
127
127
|
|
|
128
128
|
// Mobile optimization
|
|
129
129
|
@media (max-width: 768px) {
|
|
130
|
-
.
|
|
130
|
+
.account-buttons {
|
|
131
131
|
padding: 2px;
|
|
132
132
|
gap: 1px;
|
|
133
133
|
|
|
@@ -146,7 +146,7 @@ h4 {
|
|
|
146
146
|
|
|
147
147
|
// iPhone vertical (portrait) specific
|
|
148
148
|
@media (max-width: 480px) and (orientation: portrait) {
|
|
149
|
-
.
|
|
149
|
+
.account-buttons {
|
|
150
150
|
padding: 2px;
|
|
151
151
|
gap: 1px;
|
|
152
152
|
|
|
@@ -177,7 +177,7 @@ import TagLabel from "@/components/Editors/TagLabel.vue";
|
|
|
177
177
|
const ui = useUIStore();
|
|
178
178
|
|
|
179
179
|
const props = defineProps({
|
|
180
|
-
|
|
180
|
+
account: { type: Object }
|
|
181
181
|
});
|
|
182
182
|
|
|
183
183
|
const copy = (txt) => {
|
|
@@ -185,10 +185,10 @@ const copy = (txt) => {
|
|
|
185
185
|
navigator.clipboard.writeText(txt);
|
|
186
186
|
ui.showSuccess("Copied text");
|
|
187
187
|
};
|
|
188
|
-
const enable = async () => await ui.addAccount({ ...props.
|
|
189
|
-
const disable = async () => await ui.addAccount({ ...props.
|
|
188
|
+
const enable = async () => await ui.addAccount({ ...props.account, enabled: true });
|
|
189
|
+
const disable = async () => await ui.addAccount({ ...props.account, enabled: false });
|
|
190
190
|
const edit = () => {
|
|
191
|
-
ui.currentlyEditing = props.
|
|
191
|
+
ui.currentlyEditing = props.account;
|
|
192
192
|
ui.toggleModal("create-account");
|
|
193
193
|
};
|
|
194
194
|
</script>
|
|
@@ -7,48 +7,77 @@
|
|
|
7
7
|
|
|
8
8
|
<div>
|
|
9
9
|
<div class="form-grid mt-7 mb-4">
|
|
10
|
-
<div class="input-wrapper col-span-8 z-
|
|
11
|
-
<label class="label-override"
|
|
12
|
-
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
<div class="input-wrapper col-span-8" style="z-index: 200 !important; position: relative">
|
|
11
|
+
<label class="label-override mb-2"
|
|
12
|
+
>Account Tag
|
|
13
|
+
<TagIcon />
|
|
14
|
+
</label>
|
|
15
|
+
<Dropdown
|
|
16
|
+
class="input-default dropdown p-4 w-full"
|
|
17
|
+
:default="ui.profile.accountTags[0]"
|
|
18
|
+
:options="ui.profile.accountTags"
|
|
19
|
+
:onClick="(f) => (creatorConfig.tag = f)"
|
|
20
|
+
:capitalize="true"
|
|
21
|
+
:allowDefault="false"
|
|
22
|
+
:chosen="creatorConfig.tag"
|
|
23
|
+
/>
|
|
22
24
|
</div>
|
|
23
25
|
|
|
24
26
|
<div class="input-wrapper col-span-4">
|
|
25
|
-
<label class="label-override"
|
|
27
|
+
<label class="label-override mb-2"
|
|
28
|
+
>Threads
|
|
29
|
+
<EditIcon />
|
|
30
|
+
</label>
|
|
26
31
|
<div :class="`input-default ${errors.includes('threads') ? 'error' : ''}`">
|
|
27
32
|
<input placeholder="1" type="number" min="1" max="50" v-model="creatorConfig.threads" />
|
|
33
|
+
<div class="input-incrementer">
|
|
34
|
+
<button @click="creatorConfig.threads++">
|
|
35
|
+
<UpIcon />
|
|
36
|
+
</button>
|
|
37
|
+
<button @click="if (creatorConfig.threads > 1) creatorConfig.threads--;">
|
|
38
|
+
<DownIcon />
|
|
39
|
+
</button>
|
|
40
|
+
</div>
|
|
28
41
|
</div>
|
|
29
42
|
</div>
|
|
30
43
|
<div class="input-wrapper col-span-8">
|
|
31
|
-
<label class="label-override"
|
|
44
|
+
<label class="label-override mb-2"
|
|
45
|
+
>Email catchall
|
|
46
|
+
<MailIcon />
|
|
47
|
+
</label>
|
|
32
48
|
<div :class="`input-default ${errors.includes('catchall') ? 'error' : ''}`">
|
|
33
|
-
<input placeholder="
|
|
49
|
+
<input placeholder="example.com" v-model="creatorConfig.catchall" />
|
|
34
50
|
</div>
|
|
35
51
|
</div>
|
|
36
52
|
<div class="input-wrapper col-span-4">
|
|
37
|
-
<label class="label-override"
|
|
53
|
+
<label class="label-override mb-2"
|
|
54
|
+
>Catchall amount
|
|
55
|
+
<BagIcon />
|
|
56
|
+
</label>
|
|
38
57
|
<div :class="`input-default ${errors.includes('number') ? 'error' : ''}`">
|
|
39
58
|
<input placeholder="1" type="number" min="0" max="5000" v-model="creatorConfig.number" />
|
|
59
|
+
<div class="input-incrementer">
|
|
60
|
+
<button @click="creatorConfig.number++">
|
|
61
|
+
<UpIcon />
|
|
62
|
+
</button>
|
|
63
|
+
<button @click="if (creatorConfig.number > 0) creatorConfig.number--;">
|
|
64
|
+
<DownIcon />
|
|
65
|
+
</button>
|
|
66
|
+
</div>
|
|
40
67
|
</div>
|
|
41
68
|
</div>
|
|
42
69
|
<div class="input-wrapper col-span-12">
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
70
|
+
<label class="label-override mb-2"
|
|
71
|
+
>Emails
|
|
72
|
+
<MailIcon />
|
|
73
|
+
</label>
|
|
46
74
|
<div :class="`${errors.includes('emails') ? 'error-border' : ''}`">
|
|
47
75
|
<textarea
|
|
48
76
|
v-model="creatorConfig.emails"
|
|
49
77
|
class="proxy-editor"
|
|
50
78
|
spellcheck="false"
|
|
51
|
-
style="max-height: 250px; min-height: 150px
|
|
79
|
+
style="max-height: 250px; min-height: 150px"
|
|
80
|
+
placeholder="Enter emails here - One per line"
|
|
52
81
|
></textarea>
|
|
53
82
|
</div>
|
|
54
83
|
</div>
|
|
@@ -83,7 +112,7 @@
|
|
|
83
112
|
width: 100%;
|
|
84
113
|
background-color: #2e2f34;
|
|
85
114
|
color: #e2e2e5;
|
|
86
|
-
font-family:
|
|
115
|
+
font-family: "JetBrains Mono", "Fira Code", "Menlo", "Monaco", "Courier New", monospace;
|
|
87
116
|
padding: 12px;
|
|
88
117
|
border: none;
|
|
89
118
|
resize: none;
|
|
@@ -97,8 +126,8 @@
|
|
|
97
126
|
}
|
|
98
127
|
|
|
99
128
|
.proxy-editor:focus {
|
|
100
|
-
border-color: #
|
|
101
|
-
box-shadow: 0 0 0
|
|
129
|
+
border-color: #44454b;
|
|
130
|
+
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.2);
|
|
102
131
|
}
|
|
103
132
|
|
|
104
133
|
.z-inf {
|
|
@@ -107,7 +136,7 @@
|
|
|
107
136
|
</style>
|
|
108
137
|
<script setup>
|
|
109
138
|
import Modal from "@/components/ui/Modal.vue";
|
|
110
|
-
import { EditIcon } from "@/components/icons";
|
|
139
|
+
import { EditIcon, TagIcon, SpinnerIcon, UpIcon, DownIcon, MailIcon, BagIcon } from "@/components/icons";
|
|
111
140
|
import { useUIStore } from "@/stores/ui";
|
|
112
141
|
import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
|
|
113
142
|
|
|
@@ -30,31 +30,34 @@
|
|
|
30
30
|
<h4 class="hidden ipadlg:flex">Actions</h4>
|
|
31
31
|
</div>
|
|
32
32
|
</Header>
|
|
33
|
-
<div v-if="toRender.length
|
|
33
|
+
<div v-if="toRender.length != 0">
|
|
34
34
|
<RecycleScroller
|
|
35
35
|
:items="toRender"
|
|
36
36
|
:item-size="64"
|
|
37
|
-
key-field="
|
|
38
|
-
class="scroller vue-recycle-scroller ready direction-vertical flex flex-col divide-y divide-dark-650
|
|
37
|
+
key-field="index"
|
|
38
|
+
class="scroller vue-recycle-scroller ready direction-vertical flex flex-col divide-y divide-dark-650 overflow-y-auto hidden-scrollbars overflow-x-hidden stop-pan"
|
|
39
|
+
:style="{ maxHeight: dynamicTableHeight }"
|
|
39
40
|
>
|
|
40
41
|
<template #default="props">
|
|
41
|
-
<div class="
|
|
42
|
+
<div class="account" :key="`account-${props.item._id || props.item.index}`">
|
|
42
43
|
<Account
|
|
43
|
-
@click="i[props.item.
|
|
44
|
-
:style="{ backgroundColor: props.item.index % 2 == 1 ? '#
|
|
45
|
-
:
|
|
44
|
+
@click="i[props.item.index]++"
|
|
45
|
+
:style="{ backgroundColor: props.item.index % 2 == 1 ? '#1a1b1e' : '#242529' }"
|
|
46
|
+
:account="props.item"
|
|
46
47
|
/>
|
|
47
48
|
</div>
|
|
48
49
|
</template>
|
|
49
50
|
</RecycleScroller>
|
|
50
51
|
</div>
|
|
51
|
-
<div v-else class="flex justify-center py-8 bg-dark-400 empty-state">
|
|
52
|
-
|
|
52
|
+
<div v-else class="flex flex-col items-center justify-center py-8 bg-dark-400 empty-state text-center">
|
|
53
|
+
<MailIcon class="w-12 h-12 text-dark-400 mb-3 opacity-50" />
|
|
54
|
+
<p class="text-light-400 text-sm">No accounts found</p>
|
|
55
|
+
<p class="text-light-500 text-xs mt-1">Create accounts to get started</p>
|
|
53
56
|
</div>
|
|
54
57
|
</Table>
|
|
55
58
|
</template>
|
|
56
59
|
<style lang="scss" scoped>
|
|
57
|
-
.
|
|
60
|
+
.account {
|
|
58
61
|
height: 64px;
|
|
59
62
|
}
|
|
60
63
|
h4 {
|
|
@@ -73,22 +76,81 @@ h4 {
|
|
|
73
76
|
</style>
|
|
74
77
|
<script setup>
|
|
75
78
|
import { Table, Header } from "@/components/Table";
|
|
76
|
-
import {
|
|
79
|
+
import {
|
|
80
|
+
EventIcon,
|
|
81
|
+
TicketIcon,
|
|
82
|
+
StatusIcon,
|
|
83
|
+
ClickIcon,
|
|
84
|
+
DownIcon,
|
|
85
|
+
MailIcon,
|
|
86
|
+
KeyIcon,
|
|
87
|
+
CheckmarkIcon
|
|
88
|
+
} from "@/components/icons";
|
|
77
89
|
import Account from "./Account.vue";
|
|
78
90
|
import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
|
|
79
91
|
import { useUIStore } from "@/stores/ui";
|
|
80
|
-
import { computed, ref } from "vue";
|
|
92
|
+
import { computed, ref, onMounted, onUnmounted } from "vue";
|
|
81
93
|
|
|
82
94
|
const props = defineProps({
|
|
83
|
-
|
|
95
|
+
accounts: { type: Object }
|
|
84
96
|
});
|
|
85
|
-
const i = ref({});
|
|
86
|
-
props.tasks.forEach((t) => (i.value[t._id] = 0));
|
|
87
|
-
|
|
88
97
|
const ui = useUIStore();
|
|
89
98
|
|
|
99
|
+
const i = ref({});
|
|
90
100
|
const toRender = computed(() => {
|
|
91
101
|
let c = 0;
|
|
92
|
-
|
|
102
|
+
const rendered = props.accounts.map((t) => ({ ...t, index: c++ }));
|
|
103
|
+
|
|
104
|
+
// Initialize reactive refs for click tracking
|
|
105
|
+
rendered.forEach((t) => {
|
|
106
|
+
if (t._id && !(t._id in i.value)) {
|
|
107
|
+
i.value[t._id] = 0;
|
|
108
|
+
}
|
|
109
|
+
if (!(t.index in i.value)) {
|
|
110
|
+
i.value[t.index] = 0;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return rendered;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Dynamic height calculation for perfect item fitting
|
|
118
|
+
const windowHeight = ref(window.innerHeight);
|
|
119
|
+
const windowWidth = ref(window.innerWidth);
|
|
120
|
+
|
|
121
|
+
const updateDimensions = () => {
|
|
122
|
+
windowHeight.value = window.innerHeight;
|
|
123
|
+
windowWidth.value = window.innerWidth;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
onMounted(() => {
|
|
127
|
+
window.addEventListener("resize", updateDimensions);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
onUnmounted(() => {
|
|
131
|
+
window.removeEventListener("resize", updateDimensions);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const dynamicTableHeight = computed(() => {
|
|
135
|
+
// Calculate available space for accounts table with conservative buffer
|
|
136
|
+
const headerHeight = 60; // Header + navbar
|
|
137
|
+
const titleHeight = 50; // Accounts title and controls
|
|
138
|
+
const searchHeight = 50; // Search and filter controls
|
|
139
|
+
const margins = windowWidth.value >= 1024 ? 40 : 25;
|
|
140
|
+
const bufferSpace = 50; // Conservative buffer to prevent partial items
|
|
141
|
+
|
|
142
|
+
const totalUsedSpace = headerHeight + titleHeight + searchHeight + margins + bufferSpace;
|
|
143
|
+
const availableHeight = windowHeight.value - totalUsedSpace;
|
|
144
|
+
|
|
145
|
+
// Account row height is always 64px
|
|
146
|
+
const rowHeight = 64;
|
|
147
|
+
const minRowsToShow = 2;
|
|
148
|
+
const minHeight = minRowsToShow * rowHeight;
|
|
149
|
+
|
|
150
|
+
// Calculate exact number of complete rows that fit with conservative approach
|
|
151
|
+
const maxCompleteRows = Math.floor(Math.max(availableHeight, minHeight) / rowHeight);
|
|
152
|
+
const exactHeight = maxCompleteRows * rowHeight;
|
|
153
|
+
|
|
154
|
+
return exactHeight + "px";
|
|
93
155
|
});
|
|
94
156
|
</script>
|
|
@@ -7,27 +7,32 @@
|
|
|
7
7
|
</template>
|
|
8
8
|
|
|
9
9
|
<div>
|
|
10
|
-
<div class="
|
|
11
|
-
|
|
12
|
-
|
|
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: 200 !important; position: relative">
|
|
13
|
+
<label class="label-override mb-2">Profile Tag
|
|
13
14
|
<TagIcon />
|
|
14
15
|
</label>
|
|
15
16
|
<Dropdown
|
|
16
|
-
:class="`input-default dropdown w-full ${errors.includes('profileTag') ? 'error' : ''}`"
|
|
17
|
+
:class="`input-default dropdown p-4 w-full ${errors.includes('profileTag') ? 'error' : ''}`"
|
|
17
18
|
:default="ui.profile.accountTags[0]"
|
|
18
19
|
:options="ui.profile.accountTags"
|
|
19
20
|
:onClick="(f) => (account.tag = f)"
|
|
20
21
|
:capitalize="true"
|
|
22
|
+
:allowDefault="false"
|
|
23
|
+
:chosen="account.tag"
|
|
21
24
|
/>
|
|
22
25
|
</div>
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
|
|
27
|
+
<!-- Email -->
|
|
28
|
+
<div class="input-wrapper col-span-8 z-0">
|
|
29
|
+
<label class="label-override mb-2">Email
|
|
25
30
|
<MailIcon />
|
|
26
31
|
</label>
|
|
27
|
-
<div :class="`input-default
|
|
32
|
+
<div :class="`input-default ${errors.includes('email') ? 'error' : ''}`">
|
|
28
33
|
<input
|
|
29
34
|
placeholder="email@example.com"
|
|
30
|
-
type="
|
|
35
|
+
type="email"
|
|
31
36
|
v-model="account.email"
|
|
32
37
|
required
|
|
33
38
|
autocomplete="new-password"
|
|
@@ -41,28 +46,32 @@
|
|
|
41
46
|
/>
|
|
42
47
|
</div>
|
|
43
48
|
</div>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
<input
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
|
|
50
|
+
<!-- Password -->
|
|
51
|
+
<div class="input-wrapper col-span-12 z-0">
|
|
52
|
+
<label class="label-override mb-2">Password
|
|
53
|
+
<KeyIcon />
|
|
54
|
+
</label>
|
|
55
|
+
<div :class="`input-default ${errors.includes('password') ? 'error' : ''}`">
|
|
56
|
+
<input
|
|
57
|
+
placeholder="***********"
|
|
58
|
+
type="password"
|
|
59
|
+
v-model="account.password"
|
|
60
|
+
required
|
|
61
|
+
autocomplete="off"
|
|
62
|
+
name="account_password_disableautocomplete"
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
57
65
|
</div>
|
|
58
66
|
</div>
|
|
59
|
-
<button
|
|
60
|
-
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"
|
|
61
|
-
@click="done()"
|
|
62
|
-
>
|
|
63
|
-
Save <EditIcon />
|
|
64
|
-
</button>
|
|
65
67
|
</div>
|
|
68
|
+
|
|
69
|
+
<button
|
|
70
|
+
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"
|
|
71
|
+
@click="done()"
|
|
72
|
+
>
|
|
73
|
+
Save <EditIcon />
|
|
74
|
+
</button>
|
|
66
75
|
</Modal>
|
|
67
76
|
</template>
|
|
68
77
|
<style lang="scss" scoped>
|
|
@@ -71,6 +80,16 @@
|
|
|
71
80
|
@apply flex;
|
|
72
81
|
}
|
|
73
82
|
}
|
|
83
|
+
.z-0 {
|
|
84
|
+
z-index: 0 !important;
|
|
85
|
+
}
|
|
86
|
+
.z-1 {
|
|
87
|
+
z-index: 1 !important;
|
|
88
|
+
}
|
|
89
|
+
.z-2 {
|
|
90
|
+
z-index: 2 !important;
|
|
91
|
+
}
|
|
92
|
+
|
|
74
93
|
.error {
|
|
75
94
|
border-width: 2px !important;
|
|
76
95
|
border-color: rgb(238 130 130) !important;
|
|
@@ -78,7 +97,7 @@
|
|
|
78
97
|
</style>
|
|
79
98
|
<script setup>
|
|
80
99
|
import Modal from "@/components/ui/Modal.vue";
|
|
81
|
-
import { EditIcon, MailIcon, KeyIcon, ProfileIcon, TimerIcon, SandclockIcon, TagIcon } from "@/components/icons";
|
|
100
|
+
import { EditIcon, MailIcon, KeyIcon, ProfileIcon, TimerIcon, SandclockIcon, TagIcon, ScannerIcon } from "@/components/icons";
|
|
82
101
|
import { useUIStore } from "@/stores/ui";
|
|
83
102
|
import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
|
|
84
103
|
|
|
@@ -7,21 +7,21 @@
|
|
|
7
7
|
<div class="col-span-3 lg:col-span-2 flex">
|
|
8
8
|
<Checkbox
|
|
9
9
|
class="ml-0 mr-4"
|
|
10
|
-
:toggled="props.
|
|
11
|
-
@valueUpdate="ui.toggleProfileSelected(props.
|
|
10
|
+
:toggled="props.profile.selected"
|
|
11
|
+
@valueUpdate="ui.toggleProfileSelected(props.profile._id)"
|
|
12
12
|
/>
|
|
13
13
|
<h4 class="mx-auto text-white">
|
|
14
|
-
{{ props.
|
|
14
|
+
{{ props.profile.profileName }}
|
|
15
15
|
</h4>
|
|
16
16
|
</div>
|
|
17
17
|
<div class="col-span-1 lg:col-span-2">
|
|
18
18
|
<h4 class="text-white flex justify-center items-center gap-2">
|
|
19
19
|
<span class="hidden ipadlg:block">{{
|
|
20
|
-
props.
|
|
21
|
-
? props.
|
|
22
|
-
"•".repeat(props.
|
|
23
|
-
props.
|
|
24
|
-
: validateCard(props.
|
|
20
|
+
props.profile.privacy
|
|
21
|
+
? props.profile.cardNumber[0] +
|
|
22
|
+
"•".repeat(props.profile.cardNumber.length - 5) +
|
|
23
|
+
props.profile.cardNumber.slice(-4)
|
|
24
|
+
: validateCard(props.profile.cardNumber).formatted
|
|
25
25
|
}}</span>
|
|
26
26
|
<img class="w-6 h-6" :src="getAccountType()" />
|
|
27
27
|
</h4>
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
<h4 class="text-white">{{ expDate() }}</h4>
|
|
31
31
|
</div>
|
|
32
32
|
<div class="col-span-1">
|
|
33
|
-
<h4 v-if="props.
|
|
33
|
+
<h4 v-if="props.profile.enabled" class="text-green-400 flex justify-center">
|
|
34
34
|
<img class="w-3 h-3 green" src="/img/controls/enable.svg" />
|
|
35
35
|
</h4>
|
|
36
36
|
<h4 v-else class="text-red-400 flex justify-center">
|
|
@@ -40,18 +40,18 @@
|
|
|
40
40
|
|
|
41
41
|
<div class="col-span-1 hidden lg:block">
|
|
42
42
|
<h4 class="text-white flex justify-center gap-1">
|
|
43
|
-
<TagLabel v-for="tag in props.
|
|
43
|
+
<TagLabel v-for="tag in props.profile.tags" :key="tag" :text="tag" />
|
|
44
44
|
</h4>
|
|
45
45
|
</div>
|
|
46
46
|
|
|
47
47
|
<div class="col-span-1 flex">
|
|
48
|
-
<ul class="
|
|
48
|
+
<ul class="profile-buttons">
|
|
49
49
|
<li>
|
|
50
50
|
<button @click="edit">
|
|
51
51
|
<EditIcon />
|
|
52
52
|
</button>
|
|
53
53
|
</li>
|
|
54
|
-
<li v-if="props.
|
|
54
|
+
<li v-if="props.profile.enabled">
|
|
55
55
|
<button @click="disable">
|
|
56
56
|
<img class="w-4 h-4" src="/img/controls/disable.svg" />
|
|
57
57
|
</button>
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
h4 {
|
|
77
77
|
@apply text-center;
|
|
78
78
|
}
|
|
79
|
-
.
|
|
79
|
+
.profile-buttons {
|
|
80
80
|
@apply flex items-center justify-center mx-auto bg-dark-500 border border-dark-650 rounded;
|
|
81
81
|
padding: 3px;
|
|
82
82
|
gap: 2px;
|
|
@@ -119,7 +119,7 @@ h4 {
|
|
|
119
119
|
font-size: 10px !important;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
.
|
|
122
|
+
.profile-buttons {
|
|
123
123
|
padding: 3px;
|
|
124
124
|
gap: 2px;
|
|
125
125
|
|
|
@@ -135,7 +135,7 @@ h4 {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
.
|
|
138
|
+
.profile-id {
|
|
139
139
|
font-size: 6px !important;
|
|
140
140
|
margin-right: -12px;
|
|
141
141
|
margin-top: 20px;
|
|
@@ -144,7 +144,7 @@ h4 {
|
|
|
144
144
|
|
|
145
145
|
// Mobile optimization
|
|
146
146
|
@media (max-width: 768px) {
|
|
147
|
-
.
|
|
147
|
+
.profile-buttons {
|
|
148
148
|
padding: 2px;
|
|
149
149
|
gap: 1px;
|
|
150
150
|
|
|
@@ -163,7 +163,7 @@ h4 {
|
|
|
163
163
|
|
|
164
164
|
// iPhone vertical (portrait) specific
|
|
165
165
|
@media (max-width: 480px) and (orientation: portrait) {
|
|
166
|
-
.
|
|
166
|
+
.profile-buttons {
|
|
167
167
|
padding: 2px;
|
|
168
168
|
gap: 1px;
|
|
169
169
|
|
|
@@ -195,23 +195,23 @@ import TagLabel from "@/components/Editors/TagLabel.vue";
|
|
|
195
195
|
const ui = useUIStore();
|
|
196
196
|
|
|
197
197
|
const props = defineProps({
|
|
198
|
-
|
|
198
|
+
profile: { type: Object }
|
|
199
199
|
});
|
|
200
200
|
|
|
201
201
|
const getAccountType = () => {
|
|
202
|
-
var cn = props.
|
|
202
|
+
var cn = props.profile.cardNumber;
|
|
203
203
|
if (cn.startsWith("4")) return `/img/banks/visa.svg`; // visa
|
|
204
204
|
else if (cn.startsWith("3")) return `/img/banks/amex.svg`; // amex
|
|
205
205
|
else if (cn.startsWith("5")) return `/img/banks/mastercard.svg`; // master
|
|
206
206
|
};
|
|
207
207
|
|
|
208
208
|
const expDate = () =>
|
|
209
|
-
props.
|
|
210
|
-
const enable = async () => await ui.addProfile({ ...props.
|
|
211
|
-
const disable = async () => await ui.addProfile({ ...props.
|
|
209
|
+
props.profile.privacy ? "••/••" : `${props.profile.expMonth}/${props.profile.expYear?.replace("20", "")}`;
|
|
210
|
+
const enable = async () => await ui.addProfile({ ...props.profile, enabled: true });
|
|
211
|
+
const disable = async () => await ui.addProfile({ ...props.profile, enabled: false });
|
|
212
212
|
const edit = () => {
|
|
213
|
-
ui.currentlyEditing = props.
|
|
213
|
+
ui.currentlyEditing = props.profile;
|
|
214
214
|
ui.toggleModal("create-profile");
|
|
215
215
|
};
|
|
216
|
-
const deleteProfile = async () => await ui.deleteProfile(props.
|
|
216
|
+
const deleteProfile = async () => await ui.deleteProfile(props.profile._id);
|
|
217
217
|
</script>
|