@necrolab/dashboard 0.5.15 → 0.5.17

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 (137) hide show
  1. package/backend/api.js +2 -3
  2. package/eslint.config.js +46 -0
  3. package/index.html +2 -1
  4. package/package.json +5 -2
  5. package/src/App.vue +70 -566
  6. package/src/assets/css/base/mixins.scss +72 -0
  7. package/src/assets/css/base/reset.scss +0 -2
  8. package/src/assets/css/base/scroll.scss +43 -36
  9. package/src/assets/css/base/typography.scss +9 -10
  10. package/src/assets/css/base/variables.scss +43 -0
  11. package/src/assets/css/components/accessibility.scss +37 -0
  12. package/src/assets/css/components/buttons.scss +61 -74
  13. package/src/assets/css/components/forms.scss +31 -32
  14. package/src/assets/css/components/headers.scss +13 -21
  15. package/src/assets/css/components/modals.scss +2 -2
  16. package/src/assets/css/components/search-groups.scss +28 -22
  17. package/src/assets/css/components/tables.scss +5 -7
  18. package/src/assets/css/components/toasts.scss +7 -7
  19. package/src/assets/css/components/utilities.scss +295 -0
  20. package/src/assets/css/main.scss +55 -139
  21. package/src/components/Auth/LoginForm.vue +7 -86
  22. package/src/components/Console/ConsoleToolbar.vue +123 -0
  23. package/src/components/Editors/Account/Account.vue +12 -12
  24. package/src/components/Editors/Account/AccountView.vue +38 -111
  25. package/src/components/Editors/Account/CreateAccount.vue +11 -61
  26. package/src/components/Editors/Account/{AccountCreator.vue → CreateAccountBatch.vue} +28 -59
  27. package/src/components/Editors/AdminFileEditor.vue +179 -0
  28. package/src/components/Editors/Profile/CreateProfile.vue +77 -150
  29. package/src/components/Editors/Profile/Profile.vue +20 -21
  30. package/src/components/Editors/Profile/ProfileCountryChooser.vue +16 -60
  31. package/src/components/Editors/Profile/ProfileView.vue +41 -116
  32. package/src/components/Editors/ProxyFileEditor.vue +86 -0
  33. package/src/components/Editors/TagLabel.vue +16 -55
  34. package/src/components/Editors/TagToggle.vue +20 -8
  35. package/src/components/Filter/Filter.vue +66 -79
  36. package/src/components/Filter/FilterPreview.vue +153 -135
  37. package/src/components/Filter/PriceSortToggle.vue +36 -43
  38. package/src/components/Table/Header.vue +1 -1
  39. package/src/components/Table/Table.vue +45 -51
  40. package/src/components/Tasks/CheckStock.vue +7 -16
  41. package/src/components/Tasks/Controls/DesktopControls.vue +15 -60
  42. package/src/components/Tasks/Controls/MobileControls.vue +5 -20
  43. package/src/components/Tasks/CreateTaskAXS.vue +20 -118
  44. package/src/components/Tasks/CreateTaskTM.vue +33 -189
  45. package/src/components/Tasks/EventDetailRow.vue +21 -0
  46. package/src/components/Tasks/MassEdit.vue +6 -16
  47. package/src/components/Tasks/QuickSettings.vue +140 -216
  48. package/src/components/Tasks/ScrapeVenue.vue +4 -13
  49. package/src/components/Tasks/Stats.vue +20 -39
  50. package/src/components/Tasks/Task.vue +64 -270
  51. package/src/components/Tasks/TaskLabel.vue +9 -3
  52. package/src/components/Tasks/TaskView.vue +45 -64
  53. package/src/components/Tasks/Utilities.vue +10 -44
  54. package/src/components/Tasks/ViewTask.vue +23 -107
  55. package/src/components/icons/Close.vue +2 -8
  56. package/src/components/icons/Gear.vue +8 -8
  57. package/src/components/icons/Hash.vue +5 -0
  58. package/src/components/icons/Key.vue +2 -8
  59. package/src/components/icons/Pencil.vue +2 -8
  60. package/src/components/icons/Profile.vue +2 -8
  61. package/src/components/icons/Sell.vue +2 -8
  62. package/src/components/icons/Spinner.vue +4 -7
  63. package/src/components/icons/Wildcard.vue +2 -8
  64. package/src/components/icons/index.js +3 -5
  65. package/src/components/ui/ActionButtonGroup.vue +113 -52
  66. package/src/components/ui/BalanceIndicator.vue +60 -0
  67. package/src/components/ui/EmptyState.vue +24 -0
  68. package/src/components/ui/EnableDisableToggle.vue +23 -0
  69. package/src/components/ui/FormField.vue +49 -49
  70. package/src/components/ui/IconLabel.vue +23 -0
  71. package/src/components/ui/InfoRow.vue +21 -54
  72. package/src/components/ui/Modal.vue +161 -54
  73. package/src/components/ui/Navbar.vue +63 -44
  74. package/src/components/ui/ReadonlyFieldsSection.vue +31 -0
  75. package/src/components/ui/ReconnectIndicator.vue +111 -124
  76. package/src/components/ui/SectionCard.vue +6 -14
  77. package/src/components/ui/Splash.vue +2 -10
  78. package/src/components/ui/StatusBadge.vue +26 -28
  79. package/src/components/ui/TaskToggle.vue +54 -0
  80. package/src/components/ui/controls/CountryChooser.vue +29 -66
  81. package/src/components/ui/controls/EyeToggle.vue +1 -1
  82. package/src/components/ui/controls/atomic/Checkbox.vue +40 -121
  83. package/src/components/ui/controls/atomic/Dropdown.vue +103 -139
  84. package/src/components/ui/controls/atomic/MultiDropdown.vue +72 -120
  85. package/src/components/ui/controls/atomic/Switch.vue +21 -84
  86. package/src/composables/useCodeEditor.js +117 -0
  87. package/src/composables/useColorMapping.js +15 -0
  88. package/src/composables/useCopyToClipboard.js +1 -1
  89. package/src/composables/useDateFormatting.js +21 -0
  90. package/src/composables/useDeviceDetection.js +14 -0
  91. package/src/composables/useDropdownPosition.js +1 -4
  92. package/src/composables/useDynamicTableHeight.js +31 -0
  93. package/src/composables/useEnableDisable.js +6 -0
  94. package/src/composables/useFilterCSS.js +71 -0
  95. package/src/composables/useFormValidation.js +92 -0
  96. package/src/composables/useGetAllTags.js +9 -0
  97. package/src/composables/useIOSViewportHandling.js +76 -0
  98. package/src/composables/useNotchHandling.js +306 -0
  99. package/src/composables/useRowSelection.js +0 -3
  100. package/src/composables/useTableRender.js +23 -0
  101. package/src/composables/useTicketPricing.js +16 -0
  102. package/src/composables/useWindowDimensions.js +21 -0
  103. package/src/composables/useZoomPrevention.js +96 -0
  104. package/src/constants/tableLayout.js +14 -0
  105. package/src/libs/Filter.js +14 -20
  106. package/src/libs/panzoom.js +1 -5
  107. package/src/libs/utils/array.js +58 -0
  108. package/src/{stores/utils.js → libs/utils/dataGeneration.js} +2 -250
  109. package/src/libs/utils/eventUrl.js +40 -0
  110. package/src/libs/utils/string.js +3 -0
  111. package/src/libs/utils/time.js +20 -0
  112. package/src/libs/utils/validation.js +64 -0
  113. package/src/main.js +0 -2
  114. package/src/stores/connection.js +1 -29
  115. package/src/stores/logger.js +6 -12
  116. package/src/stores/sampleData.js +1 -2
  117. package/src/stores/ui.js +80 -71
  118. package/src/utils/tableHelpers.js +1 -0
  119. package/src/views/Accounts.vue +19 -38
  120. package/src/views/Console.vue +74 -253
  121. package/src/views/Editor.vue +47 -1114
  122. package/src/views/FilterBuilder.vue +190 -461
  123. package/src/views/Login.vue +3 -28
  124. package/src/views/Profiles.vue +17 -32
  125. package/src/views/Tasks.vue +51 -38
  126. package/tailwind.config.js +82 -71
  127. package/workbox-config.cjs +47 -5
  128. package/docs/plans/2026-02-08-tailwind-consolidation.md +0 -2438
  129. package/exit +0 -209
  130. package/run +0 -177
  131. package/src/assets/css/base/color-fallbacks.scss +0 -10
  132. package/src/assets/img/background.svg.backup +0 -11
  133. package/src/components/icons/SquareCheck.vue +0 -18
  134. package/src/components/icons/SquareUncheck.vue +0 -18
  135. package/src/components/ui/controls/atomic/LoadingButton.vue +0 -45
  136. package/switch-branch.sh +0 -41
  137. /package/public/{reconnect-logo.png → img/reconnect-logo.png} +0 -0
@@ -5,31 +5,31 @@
5
5
  </transition>
6
6
  <transition name="reconnect-content" appear>
7
7
  <div class="reconnect-container">
8
- <div class="loading-system">
9
- <!-- Geometric loading rings -->
10
- <div class="ring-system">
11
- <div class="ring ring-outer"></div>
12
- <div class="ring ring-middle"></div>
13
- <div class="ring ring-inner"></div>
14
- <!-- Central logo -->
15
- <img :src="logoIcon" alt="Logo" class="logo" />
16
- </div>
17
-
18
- <!-- Progress indicators -->
19
- <div class="progress-dots">
20
- <div class="dot" :class="{ active: dotIndex >= 0 }"></div>
21
- <div class="dot" :class="{ active: dotIndex >= 1 }"></div>
22
- <div class="dot" :class="{ active: dotIndex >= 2 }"></div>
23
- <div class="dot" :class="{ active: dotIndex >= 3 }"></div>
24
- </div>
25
- </div>
26
-
27
- <div class="message-container">
28
- <h2 class="message-text">{{ props.message }}</h2>
29
- <div class="status-bar">
30
- <div class="status-fill"></div>
31
- </div>
32
- </div>
8
+ <div class="loading-system">
9
+ <!-- Geometric loading rings -->
10
+ <div class="ring-system">
11
+ <div class="ring-outer ring"></div>
12
+ <div class="ring-middle ring"></div>
13
+ <div class="ring-inner ring"></div>
14
+ <!-- Central logo -->
15
+ <img :src="logoIcon" alt="Logo" class="logo" />
16
+ </div>
17
+
18
+ <!-- Progress indicators -->
19
+ <div class="progress-dots">
20
+ <div class="dot" :class="{ active: dotIndex >= 0 }"></div>
21
+ <div class="dot" :class="{ active: dotIndex >= 1 }"></div>
22
+ <div class="dot" :class="{ active: dotIndex >= 2 }"></div>
23
+ <div class="dot" :class="{ active: dotIndex >= 3 }"></div>
24
+ </div>
25
+ </div>
26
+
27
+ <div class="message-container">
28
+ <h2 class="message-text">{{ props.message }}</h2>
29
+ <div class="status-bar">
30
+ <div class="status-fill"></div>
31
+ </div>
32
+ </div>
33
33
  </div>
34
34
  </transition>
35
35
  </div>
@@ -37,12 +37,10 @@
37
37
 
38
38
  <script setup>
39
39
  import { ref, onMounted, onUnmounted } from "vue";
40
- import logoIcon from "/reconnect-logo.png";
40
+ import logoIcon from "/img/reconnect-logo.png";
41
41
 
42
42
  const dotIndex = ref(0);
43
- const progressWidth = ref(0);
44
43
  let dotInterval;
45
- let progressInterval;
46
44
 
47
45
  // Animate progress dots
48
46
  const animateDots = () => {
@@ -57,7 +55,7 @@ onMounted(() => {
57
55
  // Add page fade-in effect when modal closes
58
56
  const addPageFadeIn = () => {
59
57
  // Add global style for underlying page fade-in
60
- const style = document.createElement('style');
58
+ const style = document.createElement("style");
61
59
  style.textContent = `
62
60
  .page-fade-in {
63
61
  animation: pageReveal 0.6s ease-out forwards;
@@ -76,13 +74,13 @@ const addPageFadeIn = () => {
76
74
  }
77
75
  `;
78
76
  document.head.appendChild(style);
79
-
77
+
80
78
  // Apply the class to the body
81
- document.body.classList.add('page-fade-in');
82
-
79
+ document.body.classList.add("page-fade-in");
80
+
83
81
  // Remove the class and style after animation
84
82
  setTimeout(() => {
85
- document.body.classList.remove('page-fade-in');
83
+ document.body.classList.remove("page-fade-in");
86
84
  if (document.head.contains(style)) {
87
85
  document.head.removeChild(style);
88
86
  }
@@ -104,59 +102,32 @@ const props = defineProps({
104
102
 
105
103
  <style lang="scss" scoped>
106
104
  .reconnect-overlay {
107
- position: fixed;
108
- top: -100vh;
109
- left: -100vw;
110
- right: -100vw;
111
- bottom: -100vh;
112
- z-index: 9999;
105
+ @apply fixed inset-0 z-max;
113
106
  --tw-ring-color: transparent !important;
114
107
  }
115
108
 
116
109
  .background-layer {
117
- position: absolute;
118
- top: 0;
119
- left: 0;
120
- right: 0;
121
- bottom: 0;
110
+ @apply absolute inset-0;
122
111
  background: oklch(0.1822 0 0 / 0.98);
123
112
  backdrop-filter: blur(12px);
124
113
  -webkit-backdrop-filter: blur(12px);
125
114
  }
126
115
 
127
116
  .reconnect-container {
128
- position: absolute;
129
- top: 100vh;
130
- left: 100vw;
131
- right: 100vw;
132
- bottom: 100vh;
133
- display: flex;
134
- flex-direction: column;
135
- align-items: center;
136
- justify-content: center;
137
- gap: 3rem;
117
+ @apply absolute inset-0 flex flex-col items-center justify-center gap-12;
138
118
  --tw-ring-color: transparent !important;
139
119
  }
140
120
 
141
121
  .loading-system {
142
- position: relative;
143
- display: flex;
144
- align-items: center;
145
- justify-content: center;
122
+ @apply relative flex items-center justify-center;
146
123
  }
147
124
 
148
125
  .ring-system {
149
- position: relative;
150
- width: 160px;
151
- height: 160px;
152
- display: flex;
153
- align-items: center;
154
- justify-content: center;
126
+ @apply relative flex h-40 w-40 items-center justify-center;
155
127
  }
156
128
 
157
129
  .ring {
158
- position: absolute;
159
- border-radius: 50%;
130
+ @apply absolute rounded-full;
160
131
  background: transparent !important;
161
132
  --tw-ring-color: transparent !important;
162
133
  border: 4px solid transparent;
@@ -177,7 +148,7 @@ const props = defineProps({
177
148
  .ring-middle {
178
149
  width: 120px;
179
150
  height: 120px;
180
- border-top: 4px solid oklch(0.72 0.15 145);
151
+ @apply border-t-4 border-accent-green;
181
152
  @apply border-r-4 border-r-accent-green/40;
182
153
  border-bottom: 4px solid transparent;
183
154
  border-left: 4px solid transparent;
@@ -199,109 +170,125 @@ const props = defineProps({
199
170
  }
200
171
 
201
172
  .logo {
202
- width: 72px;
203
- height: 72px;
204
- border-radius: 50%;
173
+ @apply h-20 w-20 rounded-full border-2 border-accent-green/30 shadow-sm;
205
174
  object-fit: cover;
206
175
  z-index: 10;
207
- @apply border-2 border-accent-green/30;
208
- box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
209
176
  }
210
177
 
211
178
  .progress-dots {
212
- position: absolute;
179
+ @apply absolute flex gap-2;
213
180
  bottom: -40px;
214
181
  left: 50%;
215
182
  transform: translateX(-50%);
216
- display: flex;
217
- gap: 8px;
218
183
  }
219
184
 
220
185
  .dot {
221
- width: 8px;
222
- height: 8px;
223
- border-radius: 50%;
186
+ @apply h-2 w-2 rounded-full transition-all;
224
187
  background: oklch(0.28 0 0);
225
188
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
226
-
189
+
227
190
  &.active {
228
- background: oklch(0.72 0.15 145);
191
+ @apply bg-accent-green;
229
192
  transform: scale(1.2);
230
193
  }
231
194
  }
232
195
 
233
196
  .message-container {
234
- text-align: center;
235
- max-width: 400px;
197
+ @apply max-w-96 text-center;
236
198
  }
237
199
 
238
200
  .message-text {
239
- color: oklch(0.90 0 0);
240
- font-size: 1.5rem;
241
- font-weight: 500;
242
- margin-bottom: 1rem;
243
- letter-spacing: 0.025em;
201
+ @apply mb-4 text-2xl font-medium tracking-wide;
202
+ color: oklch(0.9 0 0);
244
203
  }
245
204
 
246
205
  .status-bar {
247
- width: 300px;
248
- height: 8px;
206
+ @apply relative h-2 w-80 overflow-hidden rounded;
249
207
  background: oklch(0.22 0 0);
250
- border-radius: 4px;
251
- overflow: hidden;
252
- position: relative;
253
208
  }
254
209
 
255
210
  .status-fill {
256
- height: 100%;
257
- background: linear-gradient(90deg, oklch(0.72 0.15 145) 0%, oklch(0.78 0.12 145) 50%, oklch(0.72 0.15 145) 100%);
258
- border-radius: 4px;
211
+ @apply relative h-full overflow-hidden rounded;
259
212
  width: 30%;
213
+ background: linear-gradient(90deg, oklch(0.72 0.15 145) 0%, oklch(0.78 0.12 145) 50%, oklch(0.72 0.15 145) 100%);
260
214
  animation: infinite-slide 2s ease-in-out infinite;
261
- position: relative;
262
- overflow: hidden;
263
215
  }
264
216
 
265
217
  .status-fill::after {
266
- content: '';
267
- position: absolute;
268
- top: 0;
269
- left: -100%;
270
- width: 100%;
271
- height: 100%;
218
+ @apply absolute -left-full top-0 h-full w-full;
219
+ content: "";
272
220
  background: linear-gradient(90deg, transparent, oklch(1 0 0 / 0.2), transparent);
273
221
  animation: shimmer 2s ease-in-out infinite;
274
222
  }
275
223
 
276
224
  // Smooth beautiful animations
277
225
  @keyframes breathe-slow {
278
- 0% { transform: rotate(0deg) scale(1); opacity: 0.8; }
279
- 50% { transform: rotate(180deg) scale(1.05); opacity: 1; }
280
- 100% { transform: rotate(360deg) scale(1); opacity: 0.8; }
226
+ 0% {
227
+ transform: rotate(0deg) scale(1);
228
+ opacity: 0.8;
229
+ }
230
+ 50% {
231
+ transform: rotate(180deg) scale(1.05);
232
+ opacity: 1;
233
+ }
234
+ 100% {
235
+ transform: rotate(360deg) scale(1);
236
+ opacity: 0.8;
237
+ }
281
238
  }
282
239
 
283
240
  @keyframes breathe-medium {
284
- 0% { transform: rotate(0deg) scale(1); opacity: 0.9; }
285
- 50% { transform: rotate(-180deg) scale(0.95); opacity: 1; }
286
- 100% { transform: rotate(-360deg) scale(1); opacity: 0.9; }
241
+ 0% {
242
+ transform: rotate(0deg) scale(1);
243
+ opacity: 0.9;
244
+ }
245
+ 50% {
246
+ transform: rotate(-180deg) scale(0.95);
247
+ opacity: 1;
248
+ }
249
+ 100% {
250
+ transform: rotate(-360deg) scale(1);
251
+ opacity: 0.9;
252
+ }
287
253
  }
288
254
 
289
255
  @keyframes breathe-fast {
290
- 0% { transform: rotate(0deg) scale(1); opacity: 1; }
291
- 50% { transform: rotate(180deg) scale(1.08); opacity: 0.7; }
292
- 100% { transform: rotate(360deg) scale(1); opacity: 1; }
256
+ 0% {
257
+ transform: rotate(0deg) scale(1);
258
+ opacity: 1;
259
+ }
260
+ 50% {
261
+ transform: rotate(180deg) scale(1.08);
262
+ opacity: 0.7;
263
+ }
264
+ 100% {
265
+ transform: rotate(360deg) scale(1);
266
+ opacity: 1;
267
+ }
293
268
  }
294
269
 
295
270
  @keyframes infinite-slide {
296
- 0% { transform: translateX(-100%); }
297
- 50% { transform: translateX(250%); }
298
- 100% { transform: translateX(-100%); }
271
+ 0% {
272
+ transform: translateX(-100%);
273
+ }
274
+ 50% {
275
+ transform: translateX(250%);
276
+ }
277
+ 100% {
278
+ transform: translateX(-100%);
279
+ }
299
280
  }
300
281
 
301
282
  @keyframes shimmer {
302
- 0% { transform: translateX(-100%); }
303
- 50% { transform: translateX(100%); }
304
- 100% { transform: translateX(300%); }
283
+ 0% {
284
+ transform: translateX(-100%);
285
+ }
286
+ 50% {
287
+ transform: translateX(100%);
288
+ }
289
+ 100% {
290
+ transform: translateX(300%);
291
+ }
305
292
  }
306
293
 
307
294
  // Staged beautiful transitions
@@ -350,35 +337,35 @@ const props = defineProps({
350
337
  width: 120px;
351
338
  height: 120px;
352
339
  }
353
-
340
+
354
341
  .ring-outer {
355
342
  width: 120px;
356
343
  height: 120px;
357
344
  }
358
-
345
+
359
346
  .ring-middle {
360
347
  width: 90px;
361
348
  height: 90px;
362
349
  top: 15px;
363
350
  left: 15px;
364
351
  }
365
-
352
+
366
353
  .ring-inner {
367
354
  width: 60px;
368
355
  height: 60px;
369
356
  top: 30px;
370
357
  left: 30px;
371
358
  }
372
-
359
+
373
360
  .logo {
374
361
  width: 54px;
375
362
  height: 54px;
376
363
  }
377
-
364
+
378
365
  .message-text {
379
366
  font-size: 1.25rem;
380
367
  }
381
-
368
+
382
369
  .status-bar {
383
370
  width: 250px;
384
371
  }
@@ -1,24 +1,16 @@
1
1
  <template>
2
- <div class="section-card">
3
- <h3 v-if="title" class="section-title">{{ title }}</h3>
2
+ <div class="rounded-lg border-2 shadow-sm bg-dark-400 p-4" style="border-color: var(--color-border);">
3
+ <h3 v-if="title" class="mb-3 text-sm font-semibold text-light-300">{{ title }}</h3>
4
4
  <slot />
5
5
  </div>
6
6
  </template>
7
7
 
8
8
  <script setup>
9
9
  defineProps({
10
- title: { type: String, default: '' }
10
+ title: {
11
+ type: String,
12
+ default: ''
13
+ }
11
14
  });
12
15
  </script>
13
16
 
14
- <style lang="scss" scoped>
15
- .section-card {
16
- @apply rounded-lg border p-4;
17
- @apply bg-dark-400 border-dark-600;
18
- }
19
-
20
- .section-title {
21
- @apply text-sm font-semibold mb-3;
22
- @apply text-light-300;
23
- }
24
- </style>
@@ -1,6 +1,6 @@
1
1
  <template>
2
- <div class="bg-dark-300 h-screen w-screen z-[1000] fixed flex items-center flex-col justify-center gap-y-3">
3
- <img src="@/assets/img/logo_trans.png" class="w-[300px] mx-auto" />
2
+ <div class="bg-dark-300 h-screen w-screen z-splash fixed flex items-center flex-col justify-center gap-y-3">
3
+ <img src="@/assets/img/logo_trans.png" class="w-75 mx-auto transition-transform duration-300 hover:scale-110" />
4
4
  <SpinnerIcon />
5
5
  </div>
6
6
  </template>
@@ -9,11 +9,3 @@
9
9
  import { SpinnerIcon } from "@/components/icons";
10
10
  </script>
11
11
 
12
- <style>
13
- img {
14
- transition: 0.3s;
15
- }
16
- img:hover {
17
- transform: scale(1.1);
18
- }
19
- </style>
@@ -1,37 +1,35 @@
1
1
  <script setup>
2
- import { defineProps } from 'vue'
3
- import { CheckmarkIcon, CloseXIcon } from '@/components/icons'
2
+ import { CheckmarkIcon, CloseXIcon } from "@/components/icons";
4
3
 
5
- const props = defineProps({
6
- enabled: {
7
- type: Boolean,
8
- required: true
9
- },
10
- size: {
11
- type: String,
12
- default: 'small',
13
- validator: (value) => ['small', 'large'].includes(value)
14
- }
15
- })
4
+ defineProps({
5
+ enabled: {
6
+ type: Boolean,
7
+ required: true
8
+ },
9
+ size: {
10
+ type: String,
11
+ default: "small",
12
+ validator: (value) => ["small", "large"].includes(value)
13
+ }
14
+ });
16
15
 
17
16
  const sizeClasses = {
18
- small: 'w-6 h-6',
19
- large: 'w-[26px] h-[26px]'
20
- }
17
+ small: "w-6 h-6",
18
+ large: "w-6.5 h-6.5"
19
+ };
21
20
 
22
- const enabledClasses = 'bg-accent-green/20 border-accent-green/30'
23
- const disabledClasses = 'bg-red-500/20 border-red-500/30'
21
+ const enabledClasses = "bg-accent-green/20 border-accent-green/30";
22
+ const disabledClasses = "bg-red-500/20 border-red-500/30";
24
23
  </script>
25
24
 
26
25
  <template>
27
- <div
28
- :class="[
29
- 'flex items-center justify-center rounded-full border-2 transition-all duration-200',
30
- enabled ? enabledClasses : disabledClasses,
31
- sizeClasses[size]
32
- ]"
33
- >
34
- <CheckmarkIcon v-if="enabled" class="w-3 h-3 text-accent-green" />
35
- <CloseXIcon v-else class="w-3 h-3 text-red-500" />
36
- </div>
26
+ <div
27
+ :class="[
28
+ 'flex-center rounded-full border-2 transition-all duration-200',
29
+ enabled ? enabledClasses : disabledClasses,
30
+ sizeClasses[size]
31
+ ]">
32
+ <CheckmarkIcon v-if="enabled" class="h-3 w-3 text-accent-green" />
33
+ <CloseXIcon v-else class="h-3 w-3 text-red-500" />
34
+ </div>
37
35
  </template>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <div class="flex flex-col gap-y-2 form-field-icons">
3
+ <h4 class="mx-auto flex items-center gap-x-2 text-center text-xs text-light-500 max-tablet-lg:!text-xs">
4
+ <template v-if="responsiveLabel">
5
+ <span class="hidden xs:block">{{ label }}</span>
6
+ <span class="block xs:hidden">{{ responsiveLabel }}</span>
7
+ </template>
8
+ <template v-else>
9
+ {{ label }}
10
+ </template>
11
+ <component :is="icon" :class="iconClass" />
12
+ </h4>
13
+ <Switch class="mx-auto" v-model="model" :disabled="disabled" />
14
+ </div>
15
+ </template>
16
+
17
+ <script setup>
18
+ import { computed } from "vue";
19
+ import Switch from "@/components/ui/controls/atomic/Switch.vue";
20
+
21
+ const props = defineProps({
22
+ icon: {
23
+ type: Object,
24
+ required: true
25
+ },
26
+ label: {
27
+ type: String,
28
+ required: true
29
+ },
30
+ responsiveLabel: {
31
+ type: String,
32
+ default: ""
33
+ },
34
+ modelValue: {
35
+ type: Boolean,
36
+ required: true
37
+ },
38
+ disabled: {
39
+ type: Boolean,
40
+ default: false
41
+ },
42
+ iconClass: {
43
+ type: String,
44
+ default: "scale-90"
45
+ }
46
+ });
47
+
48
+ const emit = defineEmits(["update:modelValue"]);
49
+
50
+ const model = computed({
51
+ get: () => props.modelValue,
52
+ set: (value) => emit("update:modelValue", value)
53
+ });
54
+ </script>