@necrolab/dashboard 0.5.15 → 0.5.16
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 +140 -170
- 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 +58 -15
- package/src/assets/css/components/forms.scss +31 -32
- package/src/assets/css/components/headers.scss +12 -20
- 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 +220 -0
- package/src/assets/css/main.scss +66 -77
- package/src/components/Auth/LoginForm.vue +5 -84
- package/src/components/Editors/Account/Account.vue +8 -10
- package/src/components/Editors/Account/AccountCreator.vue +28 -59
- package/src/components/Editors/Account/AccountView.vue +38 -86
- package/src/components/Editors/Account/CreateAccount.vue +8 -50
- package/src/components/Editors/Profile/CreateProfile.vue +74 -131
- package/src/components/Editors/Profile/Profile.vue +15 -17
- package/src/components/Editors/Profile/ProfileCountryChooser.vue +16 -60
- package/src/components/Editors/Profile/ProfileView.vue +46 -96
- package/src/components/Editors/TagLabel.vue +16 -55
- package/src/components/Editors/TagToggle.vue +20 -8
- package/src/components/Filter/Filter.vue +62 -75
- package/src/components/Filter/FilterPreview.vue +161 -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 +19 -38
- package/src/components/Tasks/Task.vue +65 -268
- package/src/components/Tasks/TaskLabel.vue +9 -3
- package/src/components/Tasks/TaskView.vue +43 -63
- 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/SquareCheck.vue +2 -8
- package/src/components/icons/SquareUncheck.vue +2 -8
- package/src/components/icons/Wildcard.vue +2 -8
- package/src/components/icons/index.js +3 -1
- 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 +48 -48
- package/src/components/ui/IconLabel.vue +23 -0
- package/src/components/ui/InfoRow.vue +21 -54
- package/src/components/ui/Modal.vue +89 -56
- package/src/components/ui/Navbar.vue +60 -41
- 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 +27 -64
- 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 +71 -119
- package/src/components/ui/controls/atomic/Switch.vue +21 -84
- 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 +3 -4
- package/src/composables/useDynamicTableHeight.js +31 -0
- package/src/composables/useRowSelection.js +0 -3
- package/src/composables/useTicketPricing.js +16 -0
- package/src/composables/useWindowDimensions.js +21 -0
- package/src/libs/Filter.js +14 -20
- package/src/libs/panzoom.js +1 -5
- package/src/libs/utils/array.js +60 -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 +28 -0
- package/src/libs/utils/time.js +20 -0
- package/src/libs/utils/validation.js +88 -0
- package/src/main.js +0 -2
- package/src/stores/connection.js +1 -4
- package/src/stores/logger.js +6 -12
- package/src/stores/sampleData.js +1 -2
- package/src/stores/ui.js +59 -36
- package/src/views/Accounts.vue +13 -24
- package/src/views/Console.vue +70 -172
- package/src/views/Editor.vue +211 -379
- package/src/views/FilterBuilder.vue +188 -371
- package/src/views/Login.vue +3 -28
- package/src/views/Profiles.vue +8 -15
- package/src/views/Tasks.vue +49 -36
- 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/switch-branch.sh +0 -41
- /package/public/{reconnect-logo.png → img/reconnect-logo.png} +0 -0
|
@@ -1,41 +1,26 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="form-section">
|
|
3
3
|
<!-- Username -->
|
|
4
|
-
<div class="
|
|
4
|
+
<div class="form-field-labeled mb-3">
|
|
5
5
|
<div class="flex items-center gap-2">
|
|
6
6
|
<ProfileIcon class="w-4 h-4" />
|
|
7
7
|
<span class="text-light-300 font-medium text-sm">Username</span>
|
|
8
8
|
</div>
|
|
9
9
|
<div class="flex-1 flex items-center">
|
|
10
|
-
<input
|
|
11
|
-
type="text"
|
|
12
|
-
class="login-input"
|
|
13
|
-
v-model="user"
|
|
14
|
-
autocapitalize="off"
|
|
15
|
-
autocorrect="off"
|
|
16
|
-
autocomplete="username"
|
|
17
|
-
/>
|
|
10
|
+
<input type="text" class="form-input-minimal" v-model="user" autocapitalize="off" autocorrect="off" autocomplete="username" aria-label="Username" />
|
|
18
11
|
</div>
|
|
19
12
|
</div>
|
|
20
13
|
<!-- Password -->
|
|
21
|
-
<div class="
|
|
14
|
+
<div class="form-field-labeled mb-4">
|
|
22
15
|
<div class="flex items-center gap-2">
|
|
23
16
|
<KeyIcon class="w-4 h-4" />
|
|
24
17
|
<span class="text-light-300 font-medium text-sm">Password</span>
|
|
25
18
|
</div>
|
|
26
19
|
<div class="flex-1 flex items-center">
|
|
27
|
-
<input
|
|
28
|
-
type="password"
|
|
29
|
-
class="login-input"
|
|
30
|
-
v-model="password"
|
|
31
|
-
/>
|
|
20
|
+
<input type="password" class="form-input-minimal" v-model="password" aria-label="Password" />
|
|
32
21
|
</div>
|
|
33
22
|
</div>
|
|
34
|
-
<button
|
|
35
|
-
class="login-btn mt-6 mx-auto"
|
|
36
|
-
@click="login()"
|
|
37
|
-
:disabled="buttonDisabled"
|
|
38
|
-
>
|
|
23
|
+
<button class="login-btn w-48 mt-6 mx-auto" @click="login()" :disabled="buttonDisabled">
|
|
39
24
|
<span v-if="!buttonDisabled">Login</span>
|
|
40
25
|
<svg v-if="!buttonDisabled" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5">
|
|
41
26
|
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4M10 17l5-5-5-5M13.8 12H3"/>
|
|
@@ -68,67 +53,3 @@ async function login() {
|
|
|
68
53
|
buttonDisabled.value = false;
|
|
69
54
|
}
|
|
70
55
|
</script>
|
|
71
|
-
|
|
72
|
-
<style lang="scss" scoped>
|
|
73
|
-
.login-btn {
|
|
74
|
-
@apply flex items-center justify-center gap-2 rounded-lg transition-all duration-150;
|
|
75
|
-
background: oklch(0.72 0.15 145);
|
|
76
|
-
border: 2px solid oklch(0.72 0.15 145);
|
|
77
|
-
color: oklch(1 0 0);
|
|
78
|
-
height: 3rem;
|
|
79
|
-
width: 12rem;
|
|
80
|
-
font-size: 0.9375rem;
|
|
81
|
-
font-weight: 600;
|
|
82
|
-
letter-spacing: 0.05em;
|
|
83
|
-
text-transform: uppercase;
|
|
84
|
-
|
|
85
|
-
&:hover:not(:disabled) {
|
|
86
|
-
background: oklch(0.68 0.15 145);
|
|
87
|
-
border-color: oklch(0.68 0.15 145);
|
|
88
|
-
transform: translateY(-1px);
|
|
89
|
-
box-shadow: 0 4px 12px oklch(0.72 0.15 145 / 0.3);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
&:active:not(:disabled) {
|
|
93
|
-
transform: translateY(0);
|
|
94
|
-
box-shadow: 0 2px 4px oklch(0.72 0.15 145 / 0.2);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
&:disabled {
|
|
98
|
-
opacity: 0.7;
|
|
99
|
-
cursor: not-allowed;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
.input-container {
|
|
104
|
-
@apply text-white bg-dark-500 px-3 rounded-lg border-2 border-dark-550 flex items-center justify-between h-11;
|
|
105
|
-
overflow: visible;
|
|
106
|
-
transition: border-color 0.15s ease;
|
|
107
|
-
|
|
108
|
-
&:hover {
|
|
109
|
-
border-color: oklch(0.30 0 0);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
&:focus-within {
|
|
113
|
-
border-color: oklch(0.72 0.15 145) !important;
|
|
114
|
-
outline: 1px solid oklch(0.72 0.15 145);
|
|
115
|
-
outline-offset: 0;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
.login-input {
|
|
120
|
-
@apply w-full h-full text-sm text-white bg-transparent border-0 outline-none px-2 py-1;
|
|
121
|
-
|
|
122
|
-
&:focus {
|
|
123
|
-
@apply outline-none border-0 shadow-none bg-transparent;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
&:hover:not(:focus) {
|
|
127
|
-
background: transparent;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
&::placeholder {
|
|
131
|
-
color: oklch(0.50 0 0);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
</style>
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
class="ml-0 mr-4"
|
|
12
12
|
:toggled="props.account.selected"
|
|
13
13
|
@valueUpdate="ui.toggleAccountSelected(props.account.id)" />
|
|
14
|
-
<h4 class="mx-auto text-white" @click="copy(props.account.email, 'Copied email')">
|
|
14
|
+
<h4 class="mx-auto text-center text-white" @click="copy(props.account.email, 'Copied email')">
|
|
15
15
|
{{ props.account.email }}
|
|
16
16
|
</h4>
|
|
17
17
|
</div>
|
|
18
18
|
<div class="col-span-2 hidden md:block" @click="copy(props.account.password, 'Copied password')">
|
|
19
|
-
<h4 class="text-white">
|
|
19
|
+
<h4 class="text-center text-white">
|
|
20
20
|
{{ props.account.privacy ? "•".repeat(props.account.password.length) : props.account.password }}
|
|
21
21
|
</h4>
|
|
22
22
|
</div>
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
</div>
|
|
26
26
|
|
|
27
27
|
<div class="col-span-1 hidden lg:block">
|
|
28
|
-
<h4 class="flex justify-center gap-1 text-white">
|
|
28
|
+
<h4 class="flex justify-center gap-1 text-center text-white">
|
|
29
29
|
<TagLabel v-for="tag in props.account.tags" :key="tag" :text="tag" />
|
|
30
30
|
</h4>
|
|
31
31
|
</div>
|
|
@@ -51,14 +51,9 @@
|
|
|
51
51
|
</div>
|
|
52
52
|
</Row>
|
|
53
53
|
</template>
|
|
54
|
-
<style lang="scss" scoped>
|
|
55
|
-
h4 {
|
|
56
|
-
@apply text-center;
|
|
57
|
-
}
|
|
58
|
-
</style>
|
|
59
54
|
<script setup>
|
|
60
55
|
import { Row } from "@/components/Table";
|
|
61
|
-
import {
|
|
56
|
+
import { EditIcon } from "@/components/icons";
|
|
62
57
|
import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
|
|
63
58
|
import StatusBadge from "@/components/ui/StatusBadge.vue";
|
|
64
59
|
import ActionButtonGroup from "@/components/ui/ActionButtonGroup.vue";
|
|
@@ -71,7 +66,10 @@ const ui = useUIStore();
|
|
|
71
66
|
const { copy } = useCopyToClipboard();
|
|
72
67
|
|
|
73
68
|
const props = defineProps({
|
|
74
|
-
account: {
|
|
69
|
+
account: {
|
|
70
|
+
type: Object,
|
|
71
|
+
required: true
|
|
72
|
+
}
|
|
75
73
|
});
|
|
76
74
|
|
|
77
75
|
const enable = async () => await ui.addAccount({ ...props.account, enabled: true });
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<div>
|
|
9
9
|
<div class="form-grid mb-4 mt-7">
|
|
10
10
|
<div class="input-wrapper relative-positioned z-tooltip col-span-8">
|
|
11
|
-
<label class="label-override mb-2">
|
|
11
|
+
<label class="label-override mb-2 flex">
|
|
12
12
|
Account Tag
|
|
13
13
|
<TagIcon />
|
|
14
14
|
</label>
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
</div>
|
|
24
24
|
|
|
25
25
|
<div class="input-wrapper col-span-4">
|
|
26
|
-
<label class="label-override mb-2">
|
|
26
|
+
<label class="label-override mb-2 flex">
|
|
27
27
|
Threads
|
|
28
28
|
<EditIcon />
|
|
29
29
|
</label>
|
|
30
|
-
<div :class="`input-default ${errors.includes('threads') ? 'error' : ''}`">
|
|
30
|
+
<div :class="`input-default ${errors.includes('threads') ? 'border-2 border-error-300' : ''}`">
|
|
31
31
|
<input placeholder="1" type="number" min="1" max="50" v-model="creatorConfig.threads" />
|
|
32
32
|
<div class="input-incrementer">
|
|
33
33
|
<button @click="creatorConfig.threads++">
|
|
@@ -40,20 +40,20 @@
|
|
|
40
40
|
</div>
|
|
41
41
|
</div>
|
|
42
42
|
<div class="input-wrapper col-span-8">
|
|
43
|
-
<label class="label-override mb-2">
|
|
43
|
+
<label class="label-override mb-2 flex">
|
|
44
44
|
Email catchall
|
|
45
45
|
<MailIcon />
|
|
46
46
|
</label>
|
|
47
|
-
<div :class="`input-default ${errors.includes('catchall') ? 'error' : ''}`">
|
|
47
|
+
<div :class="`input-default ${errors.includes('catchall') ? 'border-2 border-error-300' : ''}`">
|
|
48
48
|
<input placeholder="example.com" v-model="creatorConfig.catchall" />
|
|
49
49
|
</div>
|
|
50
50
|
</div>
|
|
51
51
|
<div class="input-wrapper col-span-4">
|
|
52
|
-
<label class="label-override mb-2">
|
|
52
|
+
<label class="label-override mb-2 flex">
|
|
53
53
|
Catchall amount
|
|
54
54
|
<BagIcon />
|
|
55
55
|
</label>
|
|
56
|
-
<div :class="`input-default ${errors.includes('number') ? 'error' : ''}`">
|
|
56
|
+
<div :class="`input-default ${errors.includes('number') ? 'border-2 border-error-300' : ''}`">
|
|
57
57
|
<input placeholder="1" type="number" min="0" max="5000" v-model="creatorConfig.number" />
|
|
58
58
|
<div class="input-incrementer">
|
|
59
59
|
<button @click="creatorConfig.number++">
|
|
@@ -66,71 +66,28 @@
|
|
|
66
66
|
</div>
|
|
67
67
|
</div>
|
|
68
68
|
<div class="input-wrapper col-span-12">
|
|
69
|
-
<label class="label-override mb-2">
|
|
69
|
+
<label class="label-override mb-2 flex">
|
|
70
70
|
Emails
|
|
71
71
|
<MailIcon />
|
|
72
72
|
</label>
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
placeholder="Enter emails here - One per line"></textarea>
|
|
80
|
-
</div>
|
|
73
|
+
<textarea
|
|
74
|
+
v-model="creatorConfig.emails"
|
|
75
|
+
:class="['textarea-emails', errors.includes('emails') && 'textarea-error']"
|
|
76
|
+
spellcheck="false"
|
|
77
|
+
style="max-height: 250px; min-height: 150px; line-height: 1.6; tab-size: 4; font-family: 'JetBrains Mono', 'Fira Code', 'Menlo', 'Monaco', 'Courier New', monospace"
|
|
78
|
+
placeholder="Enter emails here - One per line"></textarea>
|
|
81
79
|
</div>
|
|
82
80
|
</div>
|
|
83
|
-
<button
|
|
84
|
-
class="button-default ml-auto mt-4 flex w-48 items-center justify-center gap-x-2 bg-dark-400 text-xs"
|
|
85
|
-
@click="done()">
|
|
81
|
+
<button class="btn-modal ml-auto mt-4" @click="done()">
|
|
86
82
|
Start
|
|
87
83
|
<EditIcon />
|
|
88
84
|
</button>
|
|
89
85
|
</div>
|
|
90
86
|
</Modal>
|
|
91
87
|
</template>
|
|
92
|
-
<style lang="scss" scoped>
|
|
93
|
-
.input-wrapper {
|
|
94
|
-
label {
|
|
95
|
-
@apply flex;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
.error {
|
|
99
|
-
border-width: 2px !important;
|
|
100
|
-
border-color: rgb(238 130 130) !important;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
.error-border {
|
|
104
|
-
border: 2px solid rgb(238 130 130) !important;
|
|
105
|
-
border-radius: 8px;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/* Proxy editor styles */
|
|
109
|
-
.proxy-editor {
|
|
110
|
-
width: 100%;
|
|
111
|
-
background-color: oklch(0.19 0 0);
|
|
112
|
-
color: oklch(0.90 0 0);
|
|
113
|
-
font-family: "JetBrains Mono", "Fira Code", "Menlo", "Monaco", "Courier New", monospace;
|
|
114
|
-
padding: 12px;
|
|
115
|
-
border: none;
|
|
116
|
-
resize: none;
|
|
117
|
-
font-size: 14px;
|
|
118
|
-
line-height: 1.6;
|
|
119
|
-
tab-size: 4;
|
|
120
|
-
outline: none;
|
|
121
|
-
border: 1px solid oklch(0.26 0 0);
|
|
122
|
-
border-radius: 8px;
|
|
123
|
-
overflow: auto;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
.proxy-editor:focus {
|
|
127
|
-
border-color: oklch(0.28 0 0);
|
|
128
|
-
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.2);
|
|
129
|
-
}
|
|
130
|
-
</style>
|
|
131
88
|
<script setup>
|
|
132
89
|
import Modal from "@/components/ui/Modal.vue";
|
|
133
|
-
import { EditIcon, TagIcon,
|
|
90
|
+
import { EditIcon, TagIcon, UpIcon, DownIcon, MailIcon, BagIcon } from "@/components/icons";
|
|
134
91
|
import { useUIStore } from "@/stores/ui";
|
|
135
92
|
import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
|
|
136
93
|
|
|
@@ -168,3 +125,15 @@ function done() {
|
|
|
168
125
|
ui.createAcconts(creatorConfig.value);
|
|
169
126
|
}
|
|
170
127
|
</script>
|
|
128
|
+
<style scoped>
|
|
129
|
+
.textarea-emails {
|
|
130
|
+
@apply w-full rounded border bg-dark-350 p-3 font-mono text-light-300;
|
|
131
|
+
@apply outline-none resize-none overflow-auto text-sm;
|
|
132
|
+
@apply border border-dark-550 hover:border-dark-650;
|
|
133
|
+
@apply focus:border-dark-700 focus:shadow-focus-ring;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.textarea-error {
|
|
137
|
+
@apply border-2 border-error-300 hover:border-error-300;
|
|
138
|
+
}
|
|
139
|
+
</style>
|
|
@@ -1,94 +1,71 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<Header class="sticky top-0 z-10 grid-cols-5
|
|
4
|
-
<div class="col-span-3 flex lg:col-span-2">
|
|
2
|
+
<div class="table-component relative box-border flex flex-col rounded-lg bg-dark-500 bg-clip-padding overflow-hidden shadow-sm">
|
|
3
|
+
<Header class="sticky top-0 z-10 grid-cols-5 text-center md:grid-cols-7">
|
|
4
|
+
<div class="col-span-3 flex items-center justify-start lg:col-span-2">
|
|
5
5
|
<Checkbox
|
|
6
|
-
class="mr-
|
|
6
|
+
class="ml-2 mr-4 flex-shrink-0"
|
|
7
7
|
:toggled="ui.mainCheckbox.accounts"
|
|
8
8
|
@valueUpdate="ui.toggleMainCheckbox('accounts')"
|
|
9
9
|
:isHeader="true" />
|
|
10
|
-
<div class="mx-auto flex items-center" @click="ui.toggleSort('eventId')">
|
|
10
|
+
<div class="mx-auto flex cursor-pointer items-center" @click="ui.toggleSort('eventId')">
|
|
11
11
|
<MailIcon class="mr-0 h-4 w-4 md:mr-3" />
|
|
12
|
-
<h4 class="hidden md:flex">Email</h4>
|
|
12
|
+
<h4 class="hidden text-white md:flex">Email</h4>
|
|
13
13
|
</div>
|
|
14
14
|
</div>
|
|
15
15
|
<div class="col-span-2 hidden items-center justify-center md:flex" v-once>
|
|
16
16
|
<KeyIcon class="mr-0 h-4 w-4 md:mr-3" />
|
|
17
|
-
<h4 class="hidden md:flex">Password</h4>
|
|
17
|
+
<h4 class="hidden text-white md:flex">Password</h4>
|
|
18
18
|
</div>
|
|
19
|
-
<div class="
|
|
19
|
+
<div class="grid-cell-center" v-once>
|
|
20
20
|
<CheckmarkIcon class="mr-0 h-4 w-4 md:mr-3" />
|
|
21
|
-
<h4 class="hidden md:flex">Enabled</h4>
|
|
21
|
+
<h4 class="hidden text-white md:flex">Enabled</h4>
|
|
22
22
|
</div>
|
|
23
23
|
<div class="col-span-1 hidden items-center justify-center lg:flex" v-once>
|
|
24
24
|
<TicketIcon class="mr-0 h-4 w-4 md:mr-3" />
|
|
25
|
-
<h4 class="hidden md:flex">Tags</h4>
|
|
25
|
+
<h4 class="hidden text-white md:flex">Tags</h4>
|
|
26
26
|
</div>
|
|
27
|
-
<div class="
|
|
27
|
+
<div class="grid-cell-center" v-once>
|
|
28
28
|
<ClickIcon class="mr-0 h-4 w-4 md:mr-3" />
|
|
29
|
-
<h4 class="hidden md:flex">Actions</h4>
|
|
29
|
+
<h4 class="hidden text-white md:flex">Actions</h4>
|
|
30
30
|
</div>
|
|
31
31
|
</Header>
|
|
32
32
|
<div
|
|
33
33
|
v-if="toRender.length != 0"
|
|
34
|
-
class="hidden-scrollbars
|
|
34
|
+
class="hidden-scrollbars flex flex-col divide-y divide-dark-650 overflow-y-auto overflow-x-hidden transition-colors duration-150 table-scroll"
|
|
35
35
|
:style="{ maxHeight: dynamicTableHeight }">
|
|
36
|
-
<div
|
|
36
|
+
<div
|
|
37
|
+
v-for="(account, i) in toRender"
|
|
38
|
+
:key="account.id || account.index"
|
|
39
|
+
class="min-h-16 flex-shrink-0 hover:bg-dark-550">
|
|
37
40
|
<Account
|
|
38
41
|
:class="i % 2 == 1 ? 'table-row-even' : 'table-row-odd'"
|
|
39
42
|
:account="account" />
|
|
40
43
|
</div>
|
|
41
44
|
</div>
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
<p class="text-sm text-light-400">No accounts found</p>
|
|
45
|
-
<p class="mt-1 text-xs text-light-500">Create accounts to get started</p>
|
|
46
|
-
</div>
|
|
47
|
-
</Table>
|
|
45
|
+
<EmptyState v-else :icon="MailIcon" message="No accounts found" subtitle="Create accounts to get started" />
|
|
46
|
+
</div>
|
|
48
47
|
</template>
|
|
49
|
-
<style lang="scss" scoped>
|
|
50
|
-
.account-row-container {
|
|
51
|
-
min-height: 64px;
|
|
52
|
-
flex-shrink: 0;
|
|
53
|
-
transition: background-color 0.15s ease;
|
|
54
|
-
|
|
55
|
-
&:hover {
|
|
56
|
-
@apply bg-dark-550 !important;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
h4 {
|
|
61
|
-
@apply text-white;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.stop-pan {
|
|
65
|
-
touch-action: pan-y pan-up pan-down;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.empty-state {
|
|
69
|
-
font-size: 14px;
|
|
70
|
-
font-weight: 500;
|
|
71
|
-
}
|
|
72
|
-
</style>
|
|
73
48
|
<script setup>
|
|
74
|
-
import {
|
|
49
|
+
import { Header } from "@/components/Table";
|
|
75
50
|
import {
|
|
76
|
-
EventIcon,
|
|
77
51
|
TicketIcon,
|
|
78
|
-
StatusIcon,
|
|
79
52
|
ClickIcon,
|
|
80
|
-
DownIcon,
|
|
81
53
|
MailIcon,
|
|
82
54
|
KeyIcon,
|
|
83
55
|
CheckmarkIcon
|
|
84
56
|
} from "@/components/icons";
|
|
85
57
|
import Account from "./Account.vue";
|
|
86
58
|
import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
|
|
59
|
+
import EmptyState from "@/components/ui/EmptyState.vue";
|
|
87
60
|
import { useUIStore } from "@/stores/ui";
|
|
88
|
-
import {
|
|
61
|
+
import { useDynamicTableHeight } from "@/composables/useDynamicTableHeight";
|
|
62
|
+
import { computed, ref } from "vue";
|
|
89
63
|
|
|
90
64
|
const props = defineProps({
|
|
91
|
-
accounts: {
|
|
65
|
+
accounts: {
|
|
66
|
+
type: Object,
|
|
67
|
+
required: true
|
|
68
|
+
}
|
|
92
69
|
});
|
|
93
70
|
const ui = useUIStore();
|
|
94
71
|
|
|
@@ -110,43 +87,18 @@ const toRender = computed(() => {
|
|
|
110
87
|
return rendered;
|
|
111
88
|
});
|
|
112
89
|
|
|
113
|
-
//
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
windowWidth.value = window.innerWidth;
|
|
90
|
+
// Layout constants for dynamic table height calculation
|
|
91
|
+
const LAYOUT_CONSTANTS = {
|
|
92
|
+
TOP_RESERVED_SPACE: 180, // Page header + search controls + gaps (router-wrapper handles navbar, no UTILS section)
|
|
93
|
+
BOTTOM_BUFFER: 50, // Margin at bottom to prevent overflow
|
|
94
|
+
ROW_HEIGHT: 64, // Account row height in pixels
|
|
95
|
+
MIN_ROWS_TO_SHOW: 2 // Minimum number of rows to display
|
|
120
96
|
};
|
|
121
97
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
window.removeEventListener("resize", updateDimensions);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
const dynamicTableHeight = computed(() => {
|
|
131
|
-
// Calculate available space for accounts table with conservative buffer
|
|
132
|
-
const headerHeight = 60; // Header + navbar
|
|
133
|
-
const titleHeight = 50; // Accounts title and controls
|
|
134
|
-
const searchHeight = 50; // Search and filter controls
|
|
135
|
-
const margins = windowWidth.value >= 1024 ? 40 : 25;
|
|
136
|
-
const bufferSpace = 50; // Conservative buffer to prevent partial items
|
|
137
|
-
|
|
138
|
-
const totalUsedSpace = headerHeight + titleHeight + searchHeight + margins + bufferSpace;
|
|
139
|
-
const availableHeight = windowHeight.value - totalUsedSpace;
|
|
140
|
-
|
|
141
|
-
// Account row height is always 64px
|
|
142
|
-
const rowHeight = 64;
|
|
143
|
-
const minRowsToShow = 2;
|
|
144
|
-
const minHeight = minRowsToShow * rowHeight;
|
|
145
|
-
|
|
146
|
-
// Calculate exact number of complete rows that fit with conservative approach
|
|
147
|
-
const maxCompleteRows = Math.floor(Math.max(availableHeight, minHeight) / rowHeight);
|
|
148
|
-
const exactHeight = maxCompleteRows * rowHeight;
|
|
149
|
-
|
|
150
|
-
return exactHeight + "px";
|
|
98
|
+
const { dynamicTableHeight } = useDynamicTableHeight({
|
|
99
|
+
topReservedSpace: LAYOUT_CONSTANTS.TOP_RESERVED_SPACE,
|
|
100
|
+
bottomBuffer: LAYOUT_CONSTANTS.BOTTOM_BUFFER,
|
|
101
|
+
rowHeight: LAYOUT_CONSTANTS.ROW_HEIGHT,
|
|
102
|
+
minRowsToShow: LAYOUT_CONSTANTS.MIN_ROWS_TO_SHOW
|
|
151
103
|
});
|
|
152
104
|
</script>
|
|
@@ -9,11 +9,7 @@
|
|
|
9
9
|
<div>
|
|
10
10
|
<div class="my-3 grid grid-cols-12 gap-3">
|
|
11
11
|
<!-- Account tag -->
|
|
12
|
-
<
|
|
13
|
-
<label class="label-override mb-2">
|
|
14
|
-
Account Tag
|
|
15
|
-
<TagIcon />
|
|
16
|
-
</label>
|
|
12
|
+
<FormField label="Account 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 p-4`"
|
|
19
15
|
:default="ui.profile.tags[0]"
|
|
@@ -22,10 +18,10 @@
|
|
|
22
18
|
:capitalize="true"
|
|
23
19
|
:allowDefault="false"
|
|
24
20
|
:chosen="account.tag" />
|
|
25
|
-
</
|
|
21
|
+
</FormField>
|
|
26
22
|
|
|
27
23
|
<!-- Email -->
|
|
28
|
-
<FormField label="Email" :icon="MailIcon" required :error="errors.includes('email')" z-index="0" class="col-span-8">
|
|
24
|
+
<FormField label="Email" :icon="MailIcon" required :error="errors.includes('email')" z-index="0" class="col-span-12 md:col-span-8">
|
|
29
25
|
<input
|
|
30
26
|
placeholder="email@example.com"
|
|
31
27
|
type="email"
|
|
@@ -54,47 +50,15 @@
|
|
|
54
50
|
</div>
|
|
55
51
|
|
|
56
52
|
<!-- Readonly fields when editing -->
|
|
57
|
-
<
|
|
58
|
-
<div v-if="ui.currentlyEditing.tags && ui.currentlyEditing.tags.length > 0" class="col-span-6">
|
|
59
|
-
<label class="label-override mb-2">Tags</label>
|
|
60
|
-
<div class="flex gap-2 flex-wrap">
|
|
61
|
-
<TagLabel v-for="tag in ui.currentlyEditing.tags" :key="tag" :text="tag" />
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
<div class="col-span-6">
|
|
65
|
-
<label class="label-override mb-2">Status</label>
|
|
66
|
-
<div class="flex items-center gap-3 h-10">
|
|
67
|
-
<StatusBadge :enabled="ui.currentlyEditing.enabled" size="large" />
|
|
68
|
-
<span class="text-sm font-medium" :class="ui.currentlyEditing.enabled ? 'text-green-400' : 'text-red-400'">
|
|
69
|
-
{{ ui.currentlyEditing.enabled ? 'Enabled' : 'Disabled' }}
|
|
70
|
-
</span>
|
|
71
|
-
</div>
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
53
|
+
<ReadonlyFieldsSection v-if="ui.currentlyEditing?.email" :data="ui.currentlyEditing" />
|
|
74
54
|
</div>
|
|
75
55
|
|
|
76
|
-
<button
|
|
77
|
-
class="button-default ml-auto mt-4 flex w-48 items-center justify-center gap-x-2 bg-dark-400 text-xs"
|
|
78
|
-
@click="done()">
|
|
56
|
+
<button class="btn-modal ml-auto mt-4 w-48" @click="done()">
|
|
79
57
|
Save
|
|
80
|
-
<EditIcon />
|
|
58
|
+
<EditIcon class="ml-2" />
|
|
81
59
|
</button>
|
|
82
60
|
</Modal>
|
|
83
61
|
</template>
|
|
84
|
-
<style lang="scss" scoped>
|
|
85
|
-
.label-override {
|
|
86
|
-
@apply flex items-center;
|
|
87
|
-
color: #e1e1e4 !important;
|
|
88
|
-
|
|
89
|
-
svg {
|
|
90
|
-
@apply ml-2;
|
|
91
|
-
|
|
92
|
-
path {
|
|
93
|
-
fill: #e1e1e4 !important;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
</style>
|
|
98
62
|
<script setup>
|
|
99
63
|
import Modal from "@/components/ui/Modal.vue";
|
|
100
64
|
import FormField from "@/components/ui/FormField.vue";
|
|
@@ -102,17 +66,11 @@ import {
|
|
|
102
66
|
EditIcon,
|
|
103
67
|
MailIcon,
|
|
104
68
|
KeyIcon,
|
|
105
|
-
|
|
106
|
-
TimerIcon,
|
|
107
|
-
SandclockIcon,
|
|
108
|
-
TagIcon,
|
|
109
|
-
ScannerIcon
|
|
69
|
+
TagIcon
|
|
110
70
|
} from "@/components/icons";
|
|
111
71
|
import { useUIStore } from "@/stores/ui";
|
|
112
72
|
import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
|
|
113
|
-
import
|
|
114
|
-
import TagLabel from "@/components/Editors/TagLabel.vue";
|
|
115
|
-
|
|
73
|
+
import ReadonlyFieldsSection from "@/components/ui/ReadonlyFieldsSection.vue";
|
|
116
74
|
import { ref } from "vue";
|
|
117
75
|
|
|
118
76
|
const ui = useUIStore();
|