@necrolab/dashboard 0.5.12 → 0.5.14

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 (54) hide show
  1. package/.playwright-mcp/verification-accounts-desktop.png +0 -0
  2. package/.playwright-mcp/verification-tasks-desktop.png +0 -0
  3. package/.playwright-mcp/verification-tasks-mobile.png +0 -0
  4. package/README.md +21 -0
  5. package/docs/plans/2026-02-08-tailwind-consolidation-results.md +476 -0
  6. package/docs/plans/2026-02-08-tailwind-consolidation.md +2416 -0
  7. package/package.json +1 -1
  8. package/src/App.vue +2 -163
  9. package/src/assets/css/components/buttons.scss +43 -95
  10. package/src/assets/css/components/forms.scss +10 -28
  11. package/src/assets/css/components/search-groups.scss +80 -0
  12. package/src/assets/css/components/tables.scss +0 -8
  13. package/src/assets/css/main.scss +2 -43
  14. package/src/components/Editors/Account/Account.vue +14 -220
  15. package/src/components/Editors/Account/AccountCreator.vue +0 -4
  16. package/src/components/Editors/Account/AccountView.vue +0 -1
  17. package/src/components/Editors/Account/CreateAccount.vue +36 -107
  18. package/src/components/Editors/Profile/CreateProfile.vue +46 -135
  19. package/src/components/Editors/Profile/Profile.vue +10 -213
  20. package/src/components/Editors/Profile/ProfileView.vue +0 -1
  21. package/src/components/Filter/Filter.vue +14 -17
  22. package/src/components/Filter/FilterPreview.vue +0 -6
  23. package/src/components/Table/Row.vue +1 -1
  24. package/src/components/Table/Table.vue +2 -16
  25. package/src/components/Tasks/CreateTaskAXS.vue +45 -104
  26. package/src/components/Tasks/CreateTaskTM.vue +58 -96
  27. package/src/components/Tasks/Task.vue +22 -209
  28. package/src/components/Tasks/TaskView.vue +5 -4
  29. package/src/components/Tasks/ViewTask.vue +201 -214
  30. package/src/components/icons/Copy.vue +6 -0
  31. package/src/components/icons/index.js +3 -1
  32. package/src/components/ui/ActionButtonGroup.vue +70 -0
  33. package/src/components/ui/FormField.vue +74 -0
  34. package/src/components/ui/InfoRow.vue +100 -0
  35. package/src/components/ui/Modal.vue +6 -47
  36. package/src/components/ui/Navbar.vue +15 -43
  37. package/src/components/ui/ReconnectIndicator.vue +4 -4
  38. package/src/components/ui/SectionCard.vue +24 -0
  39. package/src/components/ui/Splash.vue +1 -6
  40. package/src/components/ui/StatusBadge.vue +37 -0
  41. package/src/components/ui/controls/CountryChooser.vue +14 -58
  42. package/src/components/ui/controls/atomic/Dropdown.vue +16 -24
  43. package/src/components/ui/controls/atomic/MultiDropdown.vue +7 -1
  44. package/src/components/ui/controls/atomic/Switch.vue +13 -29
  45. package/src/composables/useCopyToClipboard.js +25 -0
  46. package/src/composables/useRowSelection.js +48 -0
  47. package/src/views/Accounts.vue +0 -81
  48. package/src/views/Console.vue +4 -21
  49. package/src/views/Editor.vue +48 -138
  50. package/src/views/FilterBuilder.vue +0 -23
  51. package/src/views/Login.vue +14 -63
  52. package/src/views/Profiles.vue +0 -82
  53. package/src/views/Tasks.vue +3 -24
  54. package/tailwind.config.js +47 -5
@@ -120,27 +120,18 @@ const chose = (f) => {
120
120
 
121
121
  <style scoped>
122
122
  .dropdown {
123
- @apply relative w-full text-white ml-auto rounded-lg ring-0 h-10;
124
- background: linear-gradient(135deg, oklch(0.18 0 0) 0%, oklch(0.20 0 0) 100%);
125
- border: 1px solid oklch(0.26 0 0);
123
+ @apply relative w-full text-white ml-auto rounded-lg ring-0 h-10 bg-dark-300 border border-dark-600;
126
124
  padding: 0.75rem;
127
125
  }
128
126
 
129
127
  .dropdown.transparent {
130
- background: transparent !important;
131
- border: none !important;
132
- box-shadow: none !important;
133
- padding: 0 !important;
134
- height: 40px !important;
128
+ @apply bg-transparent border-0 shadow-none p-0 h-10;
135
129
  }
136
130
 
137
131
  .dropdown.transparent:hover,
138
132
  .dropdown.transparent:focus-within,
139
133
  .dropdown.transparent.opened {
140
- border: none !important;
141
- background: transparent !important;
142
- box-shadow: none !important;
143
- outline: none !important;
134
+ @apply border-0 bg-transparent shadow-none outline-none;
144
135
  }
145
136
 
146
137
  .dropdown:not(.transparent):hover {
@@ -149,14 +140,13 @@ const chose = (f) => {
149
140
 
150
141
  .dropdown:not(.transparent):focus-within,
151
142
  .dropdown:not(.transparent).opened {
152
- border-color: oklch(0.72 0.15 145) !important;
153
- outline: 1px solid oklch(0.72 0.15 145) !important;
143
+ @apply border-border-focus outline outline-1 outline-border-focus;
154
144
  outline-offset: 0;
155
145
  }
156
146
 
157
147
  @media (min-width: 768px) {
158
148
  .dropdown {
159
- height: 40px;
149
+ @apply h-10;
160
150
  padding: 0.625rem;
161
151
  }
162
152
  }
@@ -176,21 +166,23 @@ const chose = (f) => {
176
166
  }
177
167
 
178
168
  .dropdown-arrow {
179
- @apply w-4 h-4 transition-all duration-300 flex-shrink-0;
169
+ @apply w-4 h-4 transition-all duration-300;
170
+ position: absolute;
171
+ right: 0.75rem;
172
+ top: 50%;
173
+ transform: translateY(-50%);
174
+ display: flex;
175
+ align-items: center;
176
+ justify-content: center;
180
177
  }
181
178
 
182
179
  .dropdown-menu-portal {
183
- @apply rounded-xl shadow-2xl;
184
- background: linear-gradient(135deg, oklch(0.18 0 0) 0%, oklch(0.20 0 0) 100%);
185
- border: 1px solid oklch(0.26 0 0);
180
+ @apply rounded-xl shadow-2xl bg-dark-300 border border-dark-600 overflow-x-auto overflow-y-auto touch-pan-x touch-pan-y;
186
181
  backdrop-filter: blur(12px);
187
182
  box-shadow: 0 20px 25px -5px oklch(0 0 0 / 0.4), 0 10px 10px -5px oklch(0 0 0 / 0.2),
188
183
  0 0 0 1px oklch(1 0 0 / 0.05);
189
- overflow-x: auto !important;
190
- overflow-y: auto !important;
191
- overscroll-behavior: contain !important;
192
- touch-action: pan-x pan-y !important;
193
- -webkit-overflow-scrolling: touch !important;
184
+ overscroll-behavior: contain;
185
+ -webkit-overflow-scrolling: touch;
194
186
  scrollbar-width: thin;
195
187
  scrollbar-color: oklch(0.35 0 0) oklch(0.19 0 0);
196
188
  z-index: 1000;
@@ -225,7 +225,10 @@ if (props.default && !selectedOptions.value.includes(props.default)) {
225
225
  }
226
226
 
227
227
  .dropdown-counter {
228
- @apply flex items-center gap-2 absolute right-2;
228
+ @apply flex items-center gap-2 absolute;
229
+ right: 0.75rem;
230
+ top: 50%;
231
+ transform: translateY(-50%);
229
232
  }
230
233
 
231
234
  .counter-badge {
@@ -235,6 +238,9 @@ if (props.default && !selectedOptions.value.includes(props.default)) {
235
238
 
236
239
  .dropdown-arrow {
237
240
  @apply min-w-4 min-h-4 transition-all duration-300;
241
+ display: flex;
242
+ align-items: center;
243
+ justify-content: center;
238
244
  }
239
245
 
240
246
  .dropdown-menu-portal {
@@ -18,39 +18,29 @@ const props = defineProps({
18
18
  <style lang="scss" scoped>
19
19
  /* iOS-style switch */
20
20
  .switch {
21
- position: relative;
22
- display: inline-block;
21
+ @apply relative inline-block;
23
22
  width: 51px;
24
23
  height: 31px;
25
24
  }
26
25
 
27
26
  /* Hide default HTML checkbox */
28
27
  .switch input {
29
- opacity: 0;
30
- width: 0;
31
- height: 0;
28
+ @apply opacity-0 w-0 h-0;
32
29
  }
33
30
 
34
31
  /* The slider */
35
32
  .slider {
36
- position: absolute;
37
- cursor: pointer;
38
- top: 0;
39
- left: 0;
40
- right: 0;
41
- bottom: 0;
33
+ @apply absolute cursor-pointer inset-0;
34
+ @apply border-2;
42
35
  background-color: oklch(0.26 0 0);
43
- border: 2px solid oklch(0.35 0 0);
36
+ border-color: oklch(0.35 0 0);
44
37
  transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
45
38
  }
46
39
 
47
40
  .slider:before {
48
- position: absolute;
41
+ @apply absolute;
42
+ @apply w-[23px] h-[23px] left-0.5 bottom-0.5;
49
43
  content: "";
50
- height: 23px;
51
- width: 23px;
52
- left: 2px;
53
- bottom: 2px;
54
44
  background-color: oklch(0.50 0 0);
55
45
  transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
56
46
  box-shadow: 0 1px 3px oklch(0 0 0 / 0.3);
@@ -70,16 +60,15 @@ input:checked + .slider:before {
70
60
  }
71
61
 
72
62
  .switch.disabled {
73
- pointer-events: none;
63
+ @apply pointer-events-none;
74
64
  }
75
65
 
76
66
  input:disabled + .slider {
77
- opacity: 0.5;
78
- cursor: not-allowed;
67
+ @apply opacity-50 cursor-not-allowed;
79
68
  }
80
69
 
81
70
  input:disabled + .slider:before {
82
- opacity: 0.7;
71
+ @apply opacity-70;
83
72
  }
84
73
 
85
74
  /* Rounded sliders */
@@ -93,20 +82,15 @@ input:disabled + .slider:before {
93
82
 
94
83
  @media (max-width: 810px) {
95
84
  .switch {
96
- width: 44px;
97
- min-width: 44px;
98
- height: 26px;
85
+ @apply w-11 min-w-11 h-[26px];
99
86
 
100
87
  .slider:before {
101
- width: 22px;
102
- height: 22px;
103
- left: 2px;
104
- bottom: 2px;
88
+ @apply w-[22px] h-[22px] left-0.5 bottom-0.5;
105
89
  }
106
90
  }
107
91
 
108
92
  input:checked + .slider:before {
109
- transform: translateX(18px);
93
+ @apply translate-x-[18px];
110
94
  }
111
95
  }
112
96
  </style>
@@ -0,0 +1,25 @@
1
+ import { useUIStore } from '@/stores/ui'
2
+
3
+ /**
4
+ * useCopyToClipboard - Composable for copying text with user feedback
5
+ *
6
+ * @returns {Object} Copy function with success notification
7
+ */
8
+ export function useCopyToClipboard() {
9
+ const ui = useUIStore()
10
+
11
+ const copy = (text, message = 'Copied to clipboard') => {
12
+ if (!text) return
13
+
14
+ navigator.clipboard.writeText(text)
15
+ .then(() => {
16
+ ui.showSuccess(message)
17
+ })
18
+ .catch((err) => {
19
+ ui.showError('Failed to copy')
20
+ console.error('Copy failed:', err)
21
+ })
22
+ }
23
+
24
+ return { copy }
25
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * useRowSelection - Composable for handling row selection via double-click/tap
3
+ *
4
+ * Provides consistent double-click and double-tap behavior for table rows.
5
+ * Prevents selection when clicking on buttons or checkboxes.
6
+ *
7
+ * @param {Function} toggleCallback - Function to call when row is double-clicked/tapped
8
+ * @returns {Object} Event handlers for @dblclick, @touchstart, @touchend
9
+ */
10
+ export function useRowSelection(toggleCallback) {
11
+ let lastTapTime = 0
12
+ const DOUBLE_TAP_DELAY = 300
13
+
14
+ const handleDoubleClick = (event) => {
15
+ // Don't trigger on button or checkbox clicks
16
+ if (event.target.closest('button') || event.target.closest('.checkbox')) {
17
+ return
18
+ }
19
+ toggleCallback()
20
+ }
21
+
22
+ const handleTouchStart = (event) => {
23
+ const currentTime = Date.now()
24
+ const tapGap = currentTime - lastTapTime
25
+
26
+ if (tapGap < DOUBLE_TAP_DELAY && tapGap > 0) {
27
+ // Don't trigger on button or checkbox taps
28
+ if (!event.target.closest('button') && !event.target.closest('.checkbox')) {
29
+ event.preventDefault()
30
+ toggleCallback()
31
+ }
32
+ }
33
+ lastTapTime = currentTime
34
+ }
35
+
36
+ const handleTouchEnd = (event) => {
37
+ // Prevent default touch behavior on buttons/checkboxes
38
+ if (event.target.closest('button') || event.target.closest('.checkbox')) {
39
+ return
40
+ }
41
+ }
42
+
43
+ return {
44
+ handleDoubleClick,
45
+ handleTouchStart,
46
+ handleTouchEnd
47
+ }
48
+ }
@@ -94,12 +94,6 @@
94
94
  </div>
95
95
  </template>
96
96
  <style lang="scss" scoped>
97
- .custom-dropdown-content {
98
- top: 2.6rem !important;
99
- left: -13px;
100
- @apply border border-dark-650;
101
- }
102
-
103
97
  /* Page buttons styling - match Tasks page */
104
98
  button.bg-dark-400 {
105
99
  &:active,
@@ -110,81 +104,6 @@ button.bg-dark-400 {
110
104
  }
111
105
  }
112
106
 
113
- /* Unified search component group */
114
- .unified-search-group {
115
- border: 2px solid oklch(0.2809 0 0);
116
- border-radius: 0.5rem;
117
- overflow: hidden;
118
- background: oklch(0.2603 0 0);
119
- transition: all 0.15s ease;
120
- display: flex;
121
-
122
- :deep(.tag-toggle),
123
- :deep(.dropdown),
124
- input {
125
- border: none !important;
126
- border-width: 0 !important;
127
- border-radius: 0 !important;
128
- height: 40px !important;
129
- background: transparent !important;
130
- box-shadow: none !important;
131
- min-width: fit-content !important;
132
- }
133
-
134
- :deep(.tag-toggle) {
135
- padding: 0;
136
- border: 0 solid transparent !important;
137
- border-top: 0 !important;
138
- border-bottom: 0 !important;
139
- border-left: 0 !important;
140
- border-right: 0 !important;
141
- }
142
-
143
- :deep(.tag-toggle button) {
144
- font-size: 0.875rem;
145
- padding: 0.5rem 0.75rem;
146
- min-width: fit-content;
147
- border: 0 solid transparent !important;
148
- border-top: 0 !important;
149
- border-bottom: 0 !important;
150
- border-left: 0 !important;
151
- border-right: 0 !important;
152
- }
153
-
154
- :deep(.dropdown) {
155
- width: 120px !important;
156
- min-width: 120px !important;
157
- }
158
-
159
- :deep(.dropdown-display) {
160
- padding: 0 0.75rem;
161
- font-size: 0.875rem;
162
- }
163
-
164
- input {
165
- padding-left: 0.75rem;
166
- padding-right: 0.75rem;
167
- flex: 1;
168
-
169
- &::placeholder {
170
- color: oklch(0.50 0 0);
171
- }
172
-
173
- &:focus {
174
- outline: none !important;
175
- }
176
- }
177
-
178
- &:hover {
179
- border-color: oklch(0.30 0 0);
180
- }
181
-
182
- &:focus-within {
183
- border-color: oklch(0.72 0.15 145);
184
- outline: 1px solid oklch(0.72 0.15 145);
185
- outline-offset: 0;
186
- }
187
- }
188
107
  .max-h-big-acc {
189
108
  max-height: calc(100vh - 14rem);
190
109
  overflow: auto;
@@ -139,31 +139,25 @@
139
139
  </template>
140
140
  <style lang="scss" scoped>
141
141
  .console-page {
142
- // Add generous bottom padding on mobile to prevent switch cutoff
143
- @media (max-width: 768px) {
144
- padding-bottom: 4rem;
145
- margin-bottom: 2rem;
146
- }
142
+ @apply pb-16 mb-8 md:pb-0 md:mb-0;
147
143
 
148
144
  @media (max-width: 480px) and (orientation: portrait) {
149
- padding-bottom: 6rem;
150
- margin-bottom: 3rem;
145
+ @apply pb-24 mb-12;
151
146
  }
152
147
  }
153
148
 
154
149
  .console-switches {
155
150
  @media (max-width: 480px) and (orientation: portrait) {
156
- margin-top: 1.5rem !important;
157
- margin-bottom: 4rem !important;
151
+ @apply mt-6 mb-16;
158
152
  }
159
153
  }
160
154
 
161
155
  .console {
162
156
  @apply relative rounded border-2 border-dark-550 bg-dark-400 p-2 lg:p-5;
157
+ @apply touch-pan-x touch-pan-y;
163
158
  height: calc(100vh - 18rem);
164
159
  scrollbar-width: thin;
165
160
  scrollbar-color: oklch(0.35 0 0) oklch(0.19 0 0);
166
- touch-action: pan-x pan-y !important;
167
161
  -webkit-overflow-scrolling: touch;
168
162
 
169
163
  // Use fixed height on mobile portrait to ensure switches are visible
@@ -293,17 +287,6 @@
293
287
  }
294
288
  }
295
289
 
296
- .text-xxs {
297
- font-size: 0.5rem;
298
- line-height: 0.8rem;
299
- }
300
-
301
- .header-wrapper {
302
- svg {
303
- width: 30px;
304
- height: 30px;
305
- }
306
- }
307
290
  /* Console-specific styles handled by utilities */
308
291
  </style>
309
292
  <script setup>