@necrolab/dashboard 0.4.39 → 0.4.41
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 +5 -1
- package/.prettierrc +14 -1
- package/backend/mock-data.js +8 -8
- package/dev-server.js +131 -44
- package/package.json +3 -2
- package/postinstall.js +25 -13
- package/src/assets/css/_input.scss +12 -12
- package/src/assets/css/main.scss +63 -10
- package/src/components/Auth/LoginForm.vue +12 -5
- package/src/components/Editors/Account/AccountCreator.vue +51 -22
- package/src/components/Editors/Account/AccountView.vue +64 -13
- package/src/components/Editors/Account/CreateAccount.vue +47 -28
- package/src/components/Editors/Profile/ProfileView.vue +46 -8
- package/src/components/Filter/FilterPreview.vue +0 -4
- package/src/components/Tasks/Stats.vue +22 -16
- package/src/components/Tasks/Task.vue +78 -47
- package/src/components/Tasks/TaskView.vue +10 -9
- package/src/stores/sampleData.js +84 -60
- package/src/stores/ui.js +30 -4
- package/src/views/Console.vue +208 -21
- package/src/views/Editor.vue +2 -6
- package/src/views/FilterBuilder.vue +28 -46
- package/src/views/Login.vue +134 -12
- package/tailwind.config.js +1 -1
- package/vite.config.js +26 -0
- package/src/assets/img/android-chrome-192x192.png +0 -0
|
@@ -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
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>
|
|
@@ -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,19 +30,20 @@
|
|
|
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>
|
|
@@ -56,7 +57,7 @@
|
|
|
56
57
|
</Table>
|
|
57
58
|
</template>
|
|
58
59
|
<style lang="scss" scoped>
|
|
59
|
-
.
|
|
60
|
+
.account {
|
|
60
61
|
height: 64px;
|
|
61
62
|
}
|
|
62
63
|
h4 {
|
|
@@ -88,18 +89,68 @@ import {
|
|
|
88
89
|
import Account from "./Account.vue";
|
|
89
90
|
import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
|
|
90
91
|
import { useUIStore } from "@/stores/ui";
|
|
91
|
-
import { computed, ref } from "vue";
|
|
92
|
+
import { computed, ref, onMounted, onUnmounted } from "vue";
|
|
92
93
|
|
|
93
94
|
const props = defineProps({
|
|
94
95
|
accounts: { type: Object }
|
|
95
96
|
});
|
|
96
|
-
const i = ref({});
|
|
97
|
-
props.accounts.forEach((t) => (i.value[t._id] = 0));
|
|
98
|
-
|
|
99
97
|
const ui = useUIStore();
|
|
100
98
|
|
|
99
|
+
const i = ref({});
|
|
101
100
|
const toRender = computed(() => {
|
|
102
101
|
let c = 0;
|
|
103
|
-
|
|
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";
|
|
104
155
|
});
|
|
105
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
|
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
class="mr-3"
|
|
7
7
|
:toggled="ui.mainCheckbox.profiles"
|
|
8
8
|
@valueUpdate="ui.toggleMainCheckbox('profiles')"
|
|
9
|
-
:isHeader="true"
|
|
10
|
-
/>
|
|
9
|
+
:isHeader="true" />
|
|
11
10
|
<div class="mx-auto flex items-center" @click="ui.toggleSort('eventId')">
|
|
12
11
|
<ProfileIcon class="mr-0 ipadlg:mr-3 w-4 h-4" />
|
|
13
12
|
<h4 class="hidden ipadlg:flex">Profile Name</h4>
|
|
@@ -19,7 +18,7 @@
|
|
|
19
18
|
</div>
|
|
20
19
|
<div class="col-span-1 flex items-center justify-center" v-once>
|
|
21
20
|
<TimerIcon class="mr-0 ipadlg:mr-3 w-4 h-4" />
|
|
22
|
-
<h4 class="hidden ipadlg:flex">
|
|
21
|
+
<h4 class="hidden ipadlg:flex">Expiration</h4>
|
|
23
22
|
</div>
|
|
24
23
|
<div class="col-span-1 flex items-center justify-center" v-once>
|
|
25
24
|
<CheckmarkIcon class="mr-0 ipadlg:mr-3 w-4 h-4" />
|
|
@@ -39,15 +38,14 @@
|
|
|
39
38
|
:items="toRender"
|
|
40
39
|
:item-size="64"
|
|
41
40
|
key-field="index"
|
|
42
|
-
class="scroller
|
|
43
|
-
|
|
41
|
+
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"
|
|
42
|
+
:style="{ maxHeight: dynamicTableHeight }">
|
|
44
43
|
<template #default="props">
|
|
45
44
|
<div class="profile" :key="`profile-${props.item._id || props.item.index}`">
|
|
46
45
|
<Profile
|
|
47
46
|
@click="i[props.item.index]++"
|
|
48
47
|
:style="{ backgroundColor: props.item.index % 2 == 1 ? '#1a1b1e' : '#242529' }"
|
|
49
|
-
:profile="props.item"
|
|
50
|
-
/>
|
|
48
|
+
:profile="props.item" />
|
|
51
49
|
</div>
|
|
52
50
|
</template>
|
|
53
51
|
</RecycleScroller>
|
|
@@ -88,7 +86,7 @@ import { CartIcon, TicketIcon, ClickIcon, TimerIcon, ProfileIcon, CheckmarkIcon
|
|
|
88
86
|
import Profile from "./Profile.vue";
|
|
89
87
|
import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
|
|
90
88
|
import { useUIStore } from "@/stores/ui";
|
|
91
|
-
import { computed, ref } from "vue";
|
|
89
|
+
import { computed, ref, onMounted, onUnmounted } from "vue";
|
|
92
90
|
|
|
93
91
|
const props = defineProps({
|
|
94
92
|
profiles: { type: Object }
|
|
@@ -113,4 +111,44 @@ const toRender = computed(() => {
|
|
|
113
111
|
|
|
114
112
|
return rendered;
|
|
115
113
|
});
|
|
114
|
+
|
|
115
|
+
// Dynamic height calculation for perfect item fitting
|
|
116
|
+
const windowHeight = ref(window.innerHeight);
|
|
117
|
+
const windowWidth = ref(window.innerWidth);
|
|
118
|
+
|
|
119
|
+
const updateDimensions = () => {
|
|
120
|
+
windowHeight.value = window.innerHeight;
|
|
121
|
+
windowWidth.value = window.innerWidth;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
onMounted(() => {
|
|
125
|
+
window.addEventListener("resize", updateDimensions);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
onUnmounted(() => {
|
|
129
|
+
window.removeEventListener("resize", updateDimensions);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const dynamicTableHeight = computed(() => {
|
|
133
|
+
// Calculate available space for profiles table with conservative buffer
|
|
134
|
+
const headerHeight = 60; // Header + navbar
|
|
135
|
+
const titleHeight = 50; // Profiles title and controls
|
|
136
|
+
const searchHeight = 50; // Search and filter controls
|
|
137
|
+
const margins = windowWidth.value >= 1024 ? 40 : 25;
|
|
138
|
+
const bufferSpace = 50; // Conservative buffer to prevent partial items
|
|
139
|
+
|
|
140
|
+
const totalUsedSpace = headerHeight + titleHeight + searchHeight + margins + bufferSpace;
|
|
141
|
+
const availableHeight = windowHeight.value - totalUsedSpace;
|
|
142
|
+
|
|
143
|
+
// Profile row height is always 64px
|
|
144
|
+
const rowHeight = 64;
|
|
145
|
+
const minRowsToShow = 2;
|
|
146
|
+
const minHeight = minRowsToShow * rowHeight;
|
|
147
|
+
|
|
148
|
+
// Calculate exact number of complete rows that fit with conservative approach
|
|
149
|
+
const maxCompleteRows = Math.floor(Math.max(availableHeight, minHeight) / rowHeight);
|
|
150
|
+
const exactHeight = maxCompleteRows * rowHeight;
|
|
151
|
+
|
|
152
|
+
return exactHeight + "px";
|
|
153
|
+
});
|
|
116
154
|
</script>
|
|
@@ -1,37 +1,43 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="flex text-white font-bold lg:gap-5 gap-1 lg:mb-2 mb-1" v-if="show" :key="key">
|
|
2
|
+
<div class="flex text-white font-bold lg:gap-5 gap-1 lg:mb-2 mb-1 min-h-[2.5rem]" v-if="show" :key="key">
|
|
3
3
|
<div
|
|
4
4
|
v-if="ui.queueStats.total"
|
|
5
|
-
class="stats-card lg:mb-2 mb-1 text-sm rounded-xl lg:p-3 p-2 lg:gap-3 gap-2 flex justify-between"
|
|
5
|
+
class="stats-card lg:mb-2 mb-1 text-sm rounded-xl lg:p-3 p-2 lg:gap-3 gap-2 flex justify-between items-center min-w-0 h-8"
|
|
6
6
|
>
|
|
7
|
-
<h2 class="font-bold text-sm flex items-center gap-1
|
|
7
|
+
<h2 class="font-bold text-sm flex items-center gap-1 whitespace-nowrap">
|
|
8
8
|
<img width="14px" src="@/assets/img/wildcard.svg" />Total MQM
|
|
9
9
|
</h2>
|
|
10
|
-
<span class="text-light-400 text-
|
|
10
|
+
<span class="text-light-400 text-xs font-black flex items-center justify-center whitespace-nowrap">{{
|
|
11
|
+
ui.queueStats.total
|
|
12
|
+
}}</span>
|
|
11
13
|
</div>
|
|
12
14
|
<div
|
|
13
15
|
v-if="ui.queueStats.queued"
|
|
14
|
-
class="stats-card lg:mb-2 mb-1 text-sm rounded-xl lg:p-3 p-2 lg:gap-3 gap-2 flex justify-between"
|
|
16
|
+
class="stats-card lg:mb-2 mb-1 text-sm rounded-xl lg:p-3 p-2 lg:gap-3 gap-2 flex justify-between items-center min-w-0 h-8"
|
|
15
17
|
>
|
|
16
|
-
<h2 class="font-bold text-sm flex items-center gap-1"><SkiIcon />Queued</h2>
|
|
17
|
-
<span class="text-light-400 text-
|
|
18
|
+
<h2 class="font-bold text-sm flex items-center gap-1 whitespace-nowrap"><SkiIcon />Queued</h2>
|
|
19
|
+
<span class="text-light-400 text-xs font-black flex items-center justify-center whitespace-nowrap">{{
|
|
20
|
+
ui.queueStats.queued
|
|
21
|
+
}}</span>
|
|
18
22
|
</div>
|
|
19
23
|
<div
|
|
20
24
|
v-if="ui.queueStats.sleeping"
|
|
21
|
-
class="stats-card lg:mb-2 mb-1 text-sm rounded-xl lg:p-3 p-2 lg:gap-3 gap-2 flex justify-between"
|
|
25
|
+
class="stats-card lg:mb-2 mb-1 text-sm rounded-xl lg:p-3 p-2 lg:gap-3 gap-2 flex justify-between items-center min-w-0 h-8"
|
|
22
26
|
>
|
|
23
|
-
<h2 class="font-bold text-sm flex items-center gap-1"><TimerIcon />Sleeping</h2>
|
|
24
|
-
<span class="text-light-400 text-
|
|
27
|
+
<h2 class="font-bold text-sm flex items-center gap-1 whitespace-nowrap"><TimerIcon />Sleeping</h2>
|
|
28
|
+
<span class="text-light-400 text-xs font-black flex items-center justify-center whitespace-nowrap">{{
|
|
29
|
+
ui.queueStats.sleeping
|
|
30
|
+
}}</span>
|
|
25
31
|
</div>
|
|
26
32
|
<div
|
|
27
33
|
v-if="ui.queueStats.nextQueuePasses.length > 0"
|
|
28
|
-
class="stats-card lg:mb-5 mb-2 rounded-xl text-sm flex justify-between lg:p-3 p-2 lg:gap-3 gap-2"
|
|
34
|
+
class="stats-card lg:mb-5 mb-2 rounded-xl text-sm flex justify-between items-center lg:p-3 p-2 lg:gap-3 gap-2 min-w-0 h-8"
|
|
29
35
|
>
|
|
30
|
-
<h2 class="font-bold flex text-sm items-center gap-1">
|
|
36
|
+
<h2 class="font-bold flex text-sm items-center gap-1 whitespace-nowrap">
|
|
31
37
|
<CartIcon /><span class="sm:block hidden">Next Passes</span>
|
|
32
38
|
<span class="sm:hidden block">Pass</span>
|
|
33
39
|
</h2>
|
|
34
|
-
<span class="text-light-400 text-
|
|
40
|
+
<span class="text-light-400 text-xs font-black flex items-center text-right truncate">{{
|
|
35
41
|
ui.queueStats.nextQueuePasses.slice(0, queuePassAmount).join(", ")
|
|
36
42
|
}}</span>
|
|
37
43
|
</div>
|
|
@@ -68,16 +74,16 @@ window.addEventListener("resize", () => (queuePassAmount.value = getQueuePassAmo
|
|
|
68
74
|
.stats-card {
|
|
69
75
|
@apply bg-dark-500 border border-dark-650 rounded;
|
|
70
76
|
transition: all 0.15s ease;
|
|
71
|
-
|
|
77
|
+
|
|
72
78
|
&:hover {
|
|
73
79
|
@apply bg-dark-550 border-dark-700;
|
|
74
80
|
}
|
|
75
|
-
|
|
81
|
+
|
|
76
82
|
h2 {
|
|
77
83
|
font-weight: 600;
|
|
78
84
|
color: #d4d4d4;
|
|
79
85
|
}
|
|
80
|
-
|
|
86
|
+
|
|
81
87
|
span {
|
|
82
88
|
font-weight: 700;
|
|
83
89
|
color: #cccccc;
|