@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.
Files changed (65) hide show
  1. package/.claude/settings.local.json +7 -1
  2. package/.prettierrc +14 -1
  3. package/backend/api.js +25 -16
  4. package/backend/auth.js +2 -2
  5. package/backend/batching.js +1 -1
  6. package/backend/endpoints.js +5 -5
  7. package/backend/index.js +2 -2
  8. package/backend/mock-data.js +27 -28
  9. package/backend/mock-src/classes/logger.js +5 -7
  10. package/backend/mock-src/classes/utils.js +3 -2
  11. package/backend/mock-src/ticketmaster.js +2 -2
  12. package/backend/validator.js +2 -2
  13. package/dev-server.js +136 -0
  14. package/index.html +1 -1
  15. package/index.js +1 -1
  16. package/package.json +8 -6
  17. package/postcss.config.js +1 -1
  18. package/postinstall.js +30 -16
  19. package/public/android-chrome-192x192.png +0 -0
  20. package/public/android-chrome-512x512.png +0 -0
  21. package/public/apple-touch-icon.png +0 -0
  22. package/public/favicon-16x16.png +0 -0
  23. package/public/favicon-32x32.png +0 -0
  24. package/public/favicon.ico +0 -0
  25. package/public/manifest.json +4 -4
  26. package/src/App.vue +471 -49
  27. package/src/assets/css/_input.scss +37 -37
  28. package/src/assets/css/main.scss +177 -30
  29. package/src/assets/img/logo_icon-old.png +0 -0
  30. package/src/assets/img/logo_icon.png +0 -0
  31. package/src/components/Auth/LoginForm.vue +12 -5
  32. package/src/components/Editors/Account/Account.vue +19 -19
  33. package/src/components/Editors/Account/AccountCreator.vue +53 -24
  34. package/src/components/Editors/Account/AccountView.vue +79 -17
  35. package/src/components/Editors/Account/CreateAccount.vue +47 -28
  36. package/src/components/Editors/Profile/Profile.vue +24 -24
  37. package/src/components/Editors/Profile/ProfileView.vue +67 -16
  38. package/src/components/Editors/TagLabel.vue +6 -7
  39. package/src/components/Filter/FilterPreview.vue +0 -4
  40. package/src/components/Table/Table.vue +15 -0
  41. package/src/components/Tasks/Controls/DesktopControls.vue +1 -1
  42. package/src/components/Tasks/CreateTaskAXS.vue +15 -15
  43. package/src/components/Tasks/CreateTaskTM.vue +5 -4
  44. package/src/components/Tasks/Stats.vue +22 -16
  45. package/src/components/Tasks/Task.vue +100 -81
  46. package/src/components/Tasks/TaskView.vue +25 -23
  47. package/src/components/Tasks/Utilities.vue +1 -1
  48. package/src/components/icons/Mail.vue +2 -2
  49. package/src/components/ui/Modal.vue +84 -15
  50. package/src/components/ui/Navbar.vue +118 -39
  51. package/src/components/ui/controls/atomic/Dropdown.vue +23 -3
  52. package/src/components/ui/controls/atomic/MultiDropdown.vue +43 -23
  53. package/src/stores/sampleData.js +89 -64
  54. package/src/stores/ui.js +30 -4
  55. package/src/views/Accounts.vue +2 -2
  56. package/src/views/Console.vue +276 -41
  57. package/src/views/Editor.vue +175 -28
  58. package/src/views/FilterBuilder.vue +45 -49
  59. package/src/views/Login.vue +134 -12
  60. package/src/views/Profiles.vue +8 -8
  61. package/src/views/Tasks.vue +51 -2
  62. package/tailwind.config.js +2 -2
  63. package/vite.config.js +34 -1
  64. package/vue.config.js +1 -1
  65. 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.task.selected"
11
- @valueUpdate="ui.toggleAccountSelected(props.task._id)"
10
+ :toggled="props.account.selected"
11
+ @valueUpdate="ui.toggleAccountSelected(props.account._id)"
12
12
  />
13
- <h4 class="mx-auto text-white" @click="copy(props.task.email)">
14
- {{ props.task.email }}
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.task.password)">
17
+ <div class="col-span-2 hidden ipadlg:block" @click="copy(props.account.password)">
18
18
  <h4 class="text-white">
19
- {{ props.task.privacy ? "•".repeat(props.task.password.length) : props.task.password }}
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.task.enabled" class="text-green-400 flex justify-center">
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.task.tags" :key="tag" :text="tag" />
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="task-buttons">
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.task.enabled">
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
- .task-buttons {
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
- .task-buttons {
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
- .task-id {
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
- .task-buttons {
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
- .task-buttons {
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
- task: { type: Object }
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.task, enabled: true });
189
- const disable = async () => await ui.addAccount({ ...props.task, enabled: false });
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.task;
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-inf">
11
- <label class="label-override">Account Tag </label>
12
- <div :class="`input-default ${errors.includes('accountTag') ? 'error' : ''}`">
13
- <Dropdown
14
- class="text-xs w-64 h-8"
15
- :default="ui.profile.accountTags[0]"
16
- :options="ui.profile.accountTags"
17
- :onClick="(f) => (creatorConfig.tag = f)"
18
- :capitalize="true"
19
- rightAmount="right-6 lg:right-2 top-2"
20
- />
21
- </div>
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">Threads</label>
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">Email catchall</label>
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="mycatchall.com" v-model="creatorConfig.catchall" />
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">Catchall amount</label>
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
- <div class="flex items-center gap-2">
44
- <label class="label-override">Emails </label>
45
- </div>
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: 'JetBrains Mono', 'Fira Code', 'Menlo', 'Monaco', 'Courier New', monospace;
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: #5d7cc0;
101
- box-shadow: 0 0 0 2px rgba(93, 124, 192, 0.25);
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 > 0">
33
+ <div v-if="toRender.length != 0">
34
34
  <RecycleScroller
35
35
  :items="toRender"
36
36
  :item-size="64"
37
- key-field="_id"
38
- class="scroller vue-recycle-scroller ready direction-vertical flex flex-col divide-y divide-dark-650 max-h-big overflow-y-auto hidden-scrollbars overflow-x-hidden stop-pan"
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="task" :key="i[props.item._id]">
42
+ <div class="account" :key="`account-${props.item._id || props.item.index}`">
42
43
  <Account
43
- @click="i[props.item._id]++"
44
- :style="{ backgroundColor: props.item.index % 2 == 1 ? '#35363c' : '#2e2f34' }"
45
- :task="props.item"
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
- No accounts found
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
- .task {
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 { EventIcon, TicketIcon, StatusIcon, ClickIcon, DownIcon, MailIcon, KeyIcon, CheckmarkIcon } from "@/components/icons";
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
- tasks: { type: Object }
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
- return props.tasks.map((t) => ({ ...t, index: c++ }));
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="my-3 grid grid-cols-12 gap-3 mt-7 mb-4">
11
- <div class="input-wrapper col-span-4 z-10">
12
- <label class="label-override">Profile Tag
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
- <div class="input-wrapper col-span-8">
24
- <label class="label-override">Email
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 required ${errors.includes('email') ? 'error' : ''}`">
32
+ <div :class="`input-default ${errors.includes('email') ? 'error' : ''}`">
28
33
  <input
29
34
  placeholder="email@example.com"
30
- type="text"
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
- </div>
45
- <div class="input-wrapper col-span-12">
46
- <label class="label-override">Password
47
- <KeyIcon />
48
- </label>
49
- <div :class="`input-default required ${errors.includes('password') ? 'error' : ''}`">
50
- <input
51
- placeholder="***********"
52
- v-model="account.password"
53
- required
54
- autocomplete="off"
55
- name="account_password_disableautocomplete"
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.task.selected"
11
- @valueUpdate="ui.toggleProfileSelected(props.task._id)"
10
+ :toggled="props.profile.selected"
11
+ @valueUpdate="ui.toggleProfileSelected(props.profile._id)"
12
12
  />
13
13
  <h4 class="mx-auto text-white">
14
- {{ props.task.profileName }}
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.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
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.task.enabled" class="text-green-400 flex justify-center">
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.task.tags" :key="tag" :text="tag" />
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="task-buttons">
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.task.enabled">
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
- .task-buttons {
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
- .task-buttons {
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
- .task-id {
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
- .task-buttons {
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
- .task-buttons {
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
- task: { type: Object }
198
+ profile: { type: Object }
199
199
  });
200
200
 
201
201
  const getAccountType = () => {
202
- var cn = props.task.cardNumber;
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.task.privacy ? "••/••" : `${props.task.expMonth}/${props.task.expYear?.replace("20", "")}`;
210
- const enable = async () => await ui.addProfile({ ...props.task, enabled: true });
211
- const disable = async () => await ui.addProfile({ ...props.task, enabled: false });
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.task;
213
+ ui.currentlyEditing = props.profile;
214
214
  ui.toggleModal("create-profile");
215
215
  };
216
- const deleteProfile = async () => await ui.deleteProfile(props.task._id);
216
+ const deleteProfile = async () => await ui.deleteProfile(props.profile._id);
217
217
  </script>