adminforth 2.4.0-next.31 → 2.4.0-next.310

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 (176) hide show
  1. package/commands/callTsProxy.js +14 -4
  2. package/commands/createApp/templates/api.ts.hbs +10 -0
  3. package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
  4. package/commands/createApp/templates/index.ts.hbs +12 -1
  5. package/commands/createApp/templates/package.json.hbs +1 -1
  6. package/commands/createApp/templates/prisma.config.ts.hbs +8 -0
  7. package/commands/createApp/templates/schema.prisma.hbs +0 -1
  8. package/commands/createApp/utils.js +10 -0
  9. package/commands/createCustomComponent/configLoader.js +17 -4
  10. package/commands/createCustomComponent/main.js +13 -7
  11. package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
  12. package/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs +28 -0
  13. package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
  14. package/commands/createPlugin/templates/package.json.hbs +1 -1
  15. package/commands/generateModels.js +30 -22
  16. package/dist/auth.d.ts +9 -1
  17. package/dist/auth.d.ts.map +1 -1
  18. package/dist/auth.js +21 -2
  19. package/dist/auth.js.map +1 -1
  20. package/dist/dataConnectors/baseConnector.d.ts +1 -1
  21. package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
  22. package/dist/dataConnectors/baseConnector.js +69 -17
  23. package/dist/dataConnectors/baseConnector.js.map +1 -1
  24. package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
  25. package/dist/dataConnectors/clickhouse.js +15 -0
  26. package/dist/dataConnectors/clickhouse.js.map +1 -1
  27. package/dist/dataConnectors/mongo.d.ts.map +1 -1
  28. package/dist/dataConnectors/mongo.js +50 -15
  29. package/dist/dataConnectors/mongo.js.map +1 -1
  30. package/dist/dataConnectors/mysql.d.ts.map +1 -1
  31. package/dist/dataConnectors/mysql.js +11 -0
  32. package/dist/dataConnectors/mysql.js.map +1 -1
  33. package/dist/dataConnectors/postgres.d.ts.map +1 -1
  34. package/dist/dataConnectors/postgres.js +43 -14
  35. package/dist/dataConnectors/postgres.js.map +1 -1
  36. package/dist/dataConnectors/sqlite.d.ts.map +1 -1
  37. package/dist/dataConnectors/sqlite.js +11 -0
  38. package/dist/dataConnectors/sqlite.js.map +1 -1
  39. package/dist/index.d.ts +12 -2
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +45 -22
  42. package/dist/index.js.map +1 -1
  43. package/dist/modules/codeInjector.d.ts +2 -0
  44. package/dist/modules/codeInjector.d.ts.map +1 -1
  45. package/dist/modules/codeInjector.js +62 -6
  46. package/dist/modules/codeInjector.js.map +1 -1
  47. package/dist/modules/configValidator.d.ts +6 -0
  48. package/dist/modules/configValidator.d.ts.map +1 -1
  49. package/dist/modules/configValidator.js +202 -25
  50. package/dist/modules/configValidator.js.map +1 -1
  51. package/dist/modules/restApi.d.ts +1 -1
  52. package/dist/modules/restApi.d.ts.map +1 -1
  53. package/dist/modules/restApi.js +172 -31
  54. package/dist/modules/restApi.js.map +1 -1
  55. package/dist/modules/styles.d.ts +499 -13
  56. package/dist/modules/styles.d.ts.map +1 -1
  57. package/dist/modules/styles.js +555 -31
  58. package/dist/modules/styles.js.map +1 -1
  59. package/dist/modules/utils.d.ts +7 -15
  60. package/dist/modules/utils.d.ts.map +1 -1
  61. package/dist/modules/utils.js +45 -68
  62. package/dist/modules/utils.js.map +1 -1
  63. package/dist/servers/express.d.ts +5 -0
  64. package/dist/servers/express.d.ts.map +1 -1
  65. package/dist/servers/express.js +40 -1
  66. package/dist/servers/express.js.map +1 -1
  67. package/dist/spa/index.html +1 -1
  68. package/dist/spa/package-lock.json +1208 -708
  69. package/dist/spa/package.json +34 -34
  70. package/dist/spa/src/App.vue +59 -174
  71. package/dist/spa/src/adminforth.ts +42 -18
  72. package/dist/spa/src/afcl/AreaChart.vue +0 -1
  73. package/dist/spa/src/afcl/BarChart.vue +2 -2
  74. package/dist/spa/src/afcl/Button.vue +6 -6
  75. package/dist/spa/src/afcl/ButtonGroup.vue +91 -0
  76. package/dist/spa/src/afcl/Card.vue +25 -0
  77. package/dist/spa/src/afcl/Checkbox.vue +21 -13
  78. package/dist/spa/src/afcl/CountryFlag.vue +4 -1
  79. package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
  80. package/dist/spa/src/afcl/Dialog.vue +47 -27
  81. package/dist/spa/src/afcl/Dropzone.vue +127 -48
  82. package/dist/spa/src/afcl/Input.vue +14 -6
  83. package/dist/spa/src/afcl/JsonViewer.vue +25 -0
  84. package/dist/spa/src/afcl/LinkButton.vue +3 -3
  85. package/dist/spa/src/afcl/PieChart.vue +5 -5
  86. package/dist/spa/src/afcl/ProgressBar.vue +7 -7
  87. package/dist/spa/src/afcl/Select.vue +82 -34
  88. package/dist/spa/src/afcl/Skeleton.vue +6 -6
  89. package/dist/spa/src/afcl/Table.vue +315 -73
  90. package/dist/spa/src/afcl/Textarea.vue +31 -0
  91. package/dist/spa/src/afcl/Toggle.vue +32 -0
  92. package/dist/spa/src/afcl/Tooltip.vue +28 -18
  93. package/dist/spa/src/afcl/VerticalTabs.vue +16 -7
  94. package/dist/spa/src/afcl/index.ts +6 -3
  95. package/dist/spa/src/components/AcceptModal.vue +48 -14
  96. package/dist/spa/src/components/Breadcrumbs.vue +5 -5
  97. package/dist/spa/src/components/CallActionWrapper.vue +15 -0
  98. package/dist/spa/src/components/ColumnValueInput.vue +38 -18
  99. package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
  100. package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
  101. package/dist/spa/src/components/CustomRangePicker.vue +37 -21
  102. package/dist/spa/src/components/ErrorMessage.vue +21 -0
  103. package/dist/spa/src/components/Filters.vue +195 -132
  104. package/dist/spa/src/components/GroupsTable.vue +9 -8
  105. package/dist/spa/src/components/MenuLink.vue +90 -23
  106. package/dist/spa/src/components/ResourceForm.vue +94 -51
  107. package/dist/spa/src/components/ResourceListTable.vue +115 -85
  108. package/dist/spa/src/components/ResourceListTableVirtual.vue +114 -80
  109. package/dist/spa/src/components/ShowTable.vue +21 -15
  110. package/dist/spa/src/components/Sidebar.vue +470 -0
  111. package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
  112. package/dist/spa/src/components/SkeleteLoader.vue +3 -3
  113. package/dist/spa/src/components/ThreeDotsMenu.vue +84 -15
  114. package/dist/spa/src/components/Toast.vue +40 -29
  115. package/dist/spa/src/components/UserMenuSettingsButton.vue +69 -0
  116. package/dist/spa/src/components/ValueRenderer.vue +44 -17
  117. package/dist/spa/src/controls/BoolToggle.vue +34 -0
  118. package/dist/spa/src/i18n.ts +5 -3
  119. package/dist/spa/src/main.ts +1 -1
  120. package/dist/spa/src/renderers/CompactField.vue +1 -1
  121. package/dist/spa/src/renderers/CompactUUID.vue +1 -1
  122. package/dist/spa/src/router/index.ts +8 -0
  123. package/dist/spa/src/shims-vue.d.ts +5 -0
  124. package/dist/spa/src/spa_types/core.ts +13 -1
  125. package/dist/spa/src/stores/core.ts +13 -1
  126. package/dist/spa/src/stores/filters.ts +33 -2
  127. package/dist/spa/src/stores/modal.ts +6 -1
  128. package/dist/spa/src/stores/toast.ts +22 -3
  129. package/dist/spa/src/types/Back.ts +163 -23
  130. package/dist/spa/src/types/Common.ts +91 -32
  131. package/dist/spa/src/types/FrontendAPI.ts +31 -5
  132. package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
  133. package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -2
  134. package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
  135. package/dist/spa/src/types/adapters/KeyValueAdapter.ts +16 -0
  136. package/dist/spa/src/types/adapters/index.ts +8 -0
  137. package/dist/spa/src/utils.ts +291 -11
  138. package/dist/spa/src/views/CreateView.vue +63 -21
  139. package/dist/spa/src/views/EditView.vue +55 -22
  140. package/dist/spa/src/views/ListView.vue +144 -87
  141. package/dist/spa/src/views/LoginView.vue +26 -35
  142. package/dist/spa/src/views/ResourceParent.vue +2 -2
  143. package/dist/spa/src/views/SettingsView.vue +121 -0
  144. package/dist/spa/src/views/ShowView.vue +83 -53
  145. package/dist/spa/src/websocket.ts +6 -1
  146. package/dist/spa/tsconfig.app.json +1 -1
  147. package/dist/spa/vite.config.ts +45 -2
  148. package/dist/types/Back.d.ts +146 -14
  149. package/dist/types/Back.d.ts.map +1 -1
  150. package/dist/types/Back.js +15 -0
  151. package/dist/types/Back.js.map +1 -1
  152. package/dist/types/Common.d.ts +106 -29
  153. package/dist/types/Common.d.ts.map +1 -1
  154. package/dist/types/Common.js.map +1 -1
  155. package/dist/types/FrontendAPI.d.ts +31 -3
  156. package/dist/types/FrontendAPI.d.ts.map +1 -1
  157. package/dist/types/FrontendAPI.js.map +1 -1
  158. package/dist/types/adapters/CaptchaAdapter.d.ts +30 -0
  159. package/dist/types/adapters/CaptchaAdapter.d.ts.map +1 -0
  160. package/dist/types/adapters/CaptchaAdapter.js +5 -0
  161. package/dist/types/adapters/CaptchaAdapter.js.map +1 -0
  162. package/dist/types/adapters/EmailAdapter.d.ts +1 -1
  163. package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
  164. package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
  165. package/dist/types/adapters/ImageVisionAdapter.js +2 -0
  166. package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
  167. package/dist/types/adapters/KeyValueAdapter.d.ts +10 -0
  168. package/dist/types/adapters/KeyValueAdapter.d.ts.map +1 -0
  169. package/dist/types/adapters/KeyValueAdapter.js +2 -0
  170. package/dist/types/adapters/KeyValueAdapter.js.map +1 -0
  171. package/dist/types/adapters/index.d.ts +9 -0
  172. package/dist/types/adapters/index.d.ts.map +1 -0
  173. package/dist/types/adapters/index.js +2 -0
  174. package/dist/types/adapters/index.js.map +1 -0
  175. package/package.json +4 -2
  176. package/dist/spa/src/types/adapters/index.js +0 -5
@@ -1,57 +1,98 @@
1
1
  <template>
2
- <!-- tag form used to reset the input (method .reset() in claer() function) -->
2
+ <!-- tag form used to reset the input (method .reset() in clear() function) -->
3
3
  <form class="flex items-center justify-center w-full"
4
4
  @dragover.prevent="dragging = true"
5
5
  @dragleave.prevent="dragging = false"
6
- @drop.prevent="dragging = false; doEmit($event.dataTransfer.files)"
6
+ @drop.prevent="dragging = false; doEmit(($event.dataTransfer as DataTransfer).files)"
7
7
  >
8
- <label :id="id" class="flex flex-col items-center justify-center w-full border-2 border-dashed rounded-lg cursor-pointer dark:hover:bg-gray-800
9
- hover:bg-gray-100 dark:hover:border-gray-500 dark:hover:bg-gray-600"
8
+ <label :id="id" class="flex flex-col items-center justify-center w-full border-2 border-dashed rounded-lg cursor-pointer
9
+ hover:bg-lightDropzoneBackgroundHover hover:border-lightDropzoneBorderHover dark:hover:border-darkDropzoneBorderHover dark:hover:bg-darkDropzoneBackgroundHover"
10
10
  :class="{
11
- 'border-blue-600 dark:border-blue-400': dragging,
12
- 'border-gray-300 dark:border-gray-600': !dragging,
13
- 'bg-blue-50 dark:bg-blue-800': dragging,
14
- 'bg-gray-50 dark:bg-gray-800': !dragging,
11
+ 'border-lightDropzoneBorderDragging dark:border-darkDropzoneBorderDragging': dragging,
12
+ 'border-lightDropzoneBorder dark:border-darkDropzoneBorder': !dragging,
13
+ 'bg-lightDropzoneBackgroundDragging dark:bg-darkDropzoneBackgroundDragging': dragging,
14
+ 'bg-lightDropzoneBackground dark:bg-darkDropzoneBackground': !dragging,
15
15
  'min-h-32 h-full': props.multiple,
16
16
  'h-32': !props.multiple,
17
17
  }"
18
18
  >
19
- <div class="flex flex-col items-center justify-center pt-5 pb-6">
20
-
21
-
22
- <svg v-if="!selectedFiles.length" class="w-8 h-8 mb-4 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 16">
23
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"/>
24
- </svg>
25
- <div v-else class="flex items-center justify-center flex-wrap gap-1 w-full mt-1 mb-4">
26
- <template v-for="file in selectedFiles">
27
- <p class="text-sm text-gray-500 dark:text-gray-400 flex items-center gap-1">
28
- <IconFileSolid class="w-5 h-5" />
29
- {{ file.name }} ({{ humanifySize(file.size) }})
30
- </p>
31
- </template>
32
-
19
+ <input
20
+ :id="id"
21
+ type="file"
22
+ class="hidden"
23
+ :accept="normalizedExtensions.join(',')"
24
+ @change="$event.target && doEmit(($event.target as HTMLInputElement).files!)"
25
+ :multiple="props.multiple || false"
26
+ />
27
+
28
+ <div class="flex flex-col items-center justify-center pt-5 pb-6">
29
+ <svg
30
+ v-if="!selectedFiles.length"
31
+ class="w-8 h-8 mb-4 text-lightDropzoneIcon dark:text-darkDropzoneIcon"
32
+ xmlns="http://www.w3.org/2000/svg"
33
+ fill="none"
34
+ viewBox="0 0 20 16"
35
+ >
36
+ <path
37
+ stroke="currentColor"
38
+ stroke-linecap="round"
39
+ stroke-linejoin="round"
40
+ stroke-width="2"
41
+ d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5
42
+ 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0
43
+ 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"
44
+ />
45
+ </svg>
46
+
47
+ <div
48
+ v-else
49
+ class="flex items-center justify-center py-1 flex-wrap gap-2 w-full gap-2 mt-1 mb-4 px-4"
50
+ >
51
+ <template v-for="(file, index) in selectedFiles" :key="index">
52
+ <div
53
+ class="text-sm text-lightDropzoneIcon dark:text-darkDropzoneIcon bg-lightDropzoneBackgroundHover dark:bg-darkDropzoneBackgroundHover rounded-md
54
+ flex items-center gap-1 px-2 py-1 group"
55
+ >
56
+ <IconFileSolid class="w-4 h-4 flex-shrink-0" />
57
+ <span class="truncate max-w-[200px]">{{ file.name }}</span>
58
+ <span class="text-xs">({{ humanifySize(file.size) }})</span>
59
+ <button
60
+ type="button"
61
+ @click.prevent.stop="removeFile(index)"
62
+ class="text-lightDropzoneIcon dark:text-darkDropzoneIcon hover:text-red-600 dark:hover:text-red-400
63
+ opacity-70 hover:opacity-100 transition-all"
64
+ :title="$t('Remove file')"
65
+ >
66
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
67
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
68
+ </svg>
69
+ </button>
33
70
  </div>
34
-
35
- <p v-if="!selectedFiles.length" class="mb-2 text-sm text-gray-500 dark:text-gray-400"><span class="font-semibold">{{ $t('Click to upload') }}</span> {{ $t('or drag and drop') }}</p>
36
- <p class="text-xs text-gray-500 dark:text-gray-400">
37
- {{ props.extensions.join(', ').toUpperCase().replace(/\./g, '') }}
38
- <template v-if="props.maxSizeBytes">
39
- (Max size: {{ humanifySize(props.maxSizeBytes) }})
40
- </template>
41
- </p>
71
+ </template>
42
72
  </div>
43
- <input :id="id" type="file" class="hidden"
44
- :accept="props.extensions.join(', ')"
45
- @change="doEmit($event.target.files)"
46
- :multiple="props.multiple || false"
47
- />
73
+
74
+ <p
75
+ v-if="!selectedFiles.length"
76
+ class="mb-2 text-sm text-lightDropzoneText dark:text-darkDropzoneText"
77
+ >
78
+ <span class="font-semibold">{{ $t('Click to upload') }}</span>
79
+ {{ $t('or drag and drop') }}
80
+ </p>
81
+
82
+ <p class="text-xs text-lightDropzoneText dark:text-darkDropzoneText">
83
+ {{ normalizedExtensions.join(', ').toUpperCase().replace(/\./g, '') }}
84
+ <template v-if="props.maxSizeBytes">
85
+ (Max size: {{ humanifySize(props.maxSizeBytes) }})
86
+ </template>
87
+ </p>
88
+ </div>
48
89
  </label>
49
- </form>
90
+ </form>
50
91
  </template>
51
92
 
52
93
  <script setup lang="ts">
53
94
  import { humanifySize } from '@/utils';
54
- import { ref, type Ref } from 'vue';
95
+ import { ref, type Ref, computed } from 'vue';
55
96
  import { IconFileSolid } from '@iconify-prerendered/vue-flowbite';
56
97
  import { watch } from 'vue';
57
98
  import adminforth from '@/adminforth';
@@ -67,25 +108,40 @@ const emit = defineEmits(['update:modelValue']);
67
108
 
68
109
  const id = `afcl-dropzone-${Math.random().toString(36).substring(7)}`;
69
110
 
111
+ const normalizedExtensions = computed(() => {
112
+ return props.extensions.map(ext => {
113
+ const trimmed = ext.trim().toLowerCase();
114
+ return trimmed.startsWith('.') ? trimmed : `.${trimmed}`;
115
+ });
116
+ });
117
+
70
118
  const selectedFiles: Ref<{
71
119
  name: string,
72
120
  size: number,
73
121
  mime: string,
74
122
  }[]> = ref([]);
75
123
 
124
+ const storedFiles: Ref<File[]> = ref([]);
125
+
76
126
  watch(() => props.modelValue, (files) => {
77
- selectedFiles.value = Array.from(files).map(file => ({
78
- name: file.name,
79
- size: file.size,
80
- mime: file.type,
81
- }));
82
- });
127
+ if (files && files.length > 0) {
128
+ selectedFiles.value = Array.from(files).map(file => ({
129
+ name: file.name,
130
+ size: file.size,
131
+ mime: file.type,
132
+ }));
133
+ storedFiles.value = Array.from(files);
134
+ } else {
135
+ selectedFiles.value = [];
136
+ storedFiles.value = [];
137
+ }
138
+ }, { immediate: true });
83
139
 
84
140
  function doEmit(filesIn: FileList) {
85
141
 
86
142
  const multiple = props.multiple || false;
87
143
  const files = Array.from(filesIn);
88
- const allowedExtensions = props.extensions.map(ext => ext.toLowerCase());
144
+ const allowedExtensions = normalizedExtensions.value;
89
145
  const maxSizeBytes = props.maxSizeBytes;
90
146
 
91
147
  if (!files.length) return;
@@ -96,6 +152,18 @@ function doEmit(filesIn: FileList) {
96
152
  const extension = file.name.split('.').pop()?.toLowerCase() || '';
97
153
  const size = file.size;
98
154
 
155
+ const isDuplicate = storedFiles.value.some(
156
+ existingFile => existingFile.name === file.name && existingFile.size === file.size
157
+ );
158
+
159
+ if (isDuplicate) {
160
+ adminforth.alert({
161
+ message: `The file "${file.name}" is already selected.`,
162
+ variant: 'warning',
163
+ });
164
+ return;
165
+ }
166
+
99
167
  if (!allowedExtensions.includes(`.${extension}`)) {
100
168
  adminforth.alert({
101
169
  message: `Sorry, the file type .${extension} is not allowed. Please upload a file with one of the following extensions: ${allowedExtensions.join(', ')}`,
@@ -110,26 +178,37 @@ function doEmit(filesIn: FileList) {
110
178
  });
111
179
  return;
112
180
  }
113
-
181
+
114
182
  validFiles.push(file);
115
183
  });
116
184
 
117
185
  if (!multiple) {
118
- validFiles.splice(1);
186
+ storedFiles.value = validFiles.slice(0, 1);
187
+ } else {
188
+ storedFiles.value = [...storedFiles.value, ...validFiles];
119
189
  }
120
- selectedFiles.value = validFiles.map(file => ({
190
+
191
+ selectedFiles.value = storedFiles.value.map(file => ({
121
192
  name: file.name,
122
193
  size: file.size,
123
194
  mime: file.type,
124
195
  }));
125
196
 
126
- emit('update:modelValue', validFiles);
197
+ emit('update:modelValue', storedFiles.value);
198
+
127
199
  }
128
200
 
129
201
  const dragging = ref(false);
130
202
 
203
+ function removeFile(index: number) {
204
+ storedFiles.value = storedFiles.value.filter((_, i) => i !== index);
205
+ selectedFiles.value = selectedFiles.value.filter((_, i) => i !== index);
206
+ emit('update:modelValue', storedFiles.value);
207
+ }
208
+
131
209
  function clear() {
132
210
  selectedFiles.value = [];
211
+ storedFiles.value = [];
133
212
  emit('update:modelValue', []);
134
213
  const form = document.getElementById(id)?.closest('form');
135
214
  form?.reset();
@@ -3,7 +3,7 @@
3
3
  <div class="afcl-input-wrapper flex z-0 relative" :class="{'opacity-50' : readonly}">
4
4
  <span
5
5
  v-if="$slots.prefix || prefix"
6
- class="inline-flex items-center px-3 text-sm text-gray-900 bg-gray-200 border border-s-0 border-gray-300 rounded-s-md dark:bg-gray-600 dark:text-gray-400 dark:border-gray-600">
6
+ class="inline-flex items-center px-3 text-sm text-lightInputText bg-lightInputBackground border border-s-0 border-lightInputBorder rounded-s-md dark:bg-darkInputBackground dark:text-darkInputText dark:border-darkInputBorder">
7
7
  <slot name="prefix">{{ prefix }}</slot>
8
8
  </span>
9
9
 
@@ -12,12 +12,12 @@
12
12
  ref="input"
13
13
  v-bind="$attrs"
14
14
  :type="type"
15
- @input="$emit('update:modelValue', $event.target?.value)"
15
+ @input="$emit('update:modelValue', type === 'number' ? Number(($event.target as HTMLInputElement)?.value) : ($event.target as HTMLInputElement)?.value)"
16
16
  :value="modelValue"
17
17
  aria-describedby="helper-text-explanation"
18
- class="afcl-input inline-flex bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-0 focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary
19
- blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 placeholder-gray-500 dark:placeholder-gray-400 dark:text-white translate-y-0"
20
- :class="{'rounded-l-md': !$slots.prefix && !prefix, 'rounded-r-md': !$slots.suffix && !suffix, 'w-full': fullWidth}"
18
+ class="afcl-input inline-flex bg-lightInputBackground border border-lightInputBorder rounded-0 focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary
19
+ blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-darkInputBackground dark:border-darkInputBorder placeholder-lightInputPlaceholderText dark:placeholder-darkInputPlaceholderText dark:text-darkInputText translate-y-0"
20
+ :class="{'rounded-l-md': !$slots.prefix && !prefix, 'rounded-r-md': !$slots.suffix && !suffix, 'w-full': fullWidth, 'text-base': isIOSDevice, 'text-sm': !isIOSDevice }"
21
21
  :disabled="readonly"
22
22
  >
23
23
 
@@ -26,7 +26,7 @@
26
26
  </div>
27
27
  <span
28
28
  v-if="$slots.suffix || suffix"
29
- class="inline-flex items-center px-3 text-sm text-gray-900 bg-gray-200 border border-s-0 border-gray-300 rounded-e-md dark:bg-gray-600 dark:text-gray-400 dark:border-gray-600 ">
29
+ class="inline-flex items-center px-3 text-sm text-lightInputText bg-lightInputBackground border border-s-0 border-lightInputBorder rounded-e-md dark:bg-darkInputBackground dark:text-darkInputText dark:border-darkInputBorder ">
30
30
  <slot name="suffix">{{ suffix }}</slot>
31
31
  </span>
32
32
 
@@ -36,6 +36,7 @@
36
36
  <script setup lang="ts">
37
37
 
38
38
  import { ref } from 'vue';
39
+ const isIOSDevice = isIOS();
39
40
 
40
41
  const props = defineProps<{
41
42
  type: string,
@@ -52,5 +53,12 @@ defineExpose({
52
53
  focus: () => input.value?.focus(),
53
54
  });
54
55
 
56
+ function isIOS() {
57
+ return (
58
+ /iPad|iPhone|iPod/.test(navigator.userAgent) ||
59
+ (navigator.userAgent.includes('Mac') && 'ontouchend' in document)
60
+ )
61
+ }
62
+
55
63
  </script>
56
64
 
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <JsonViewer
3
+ class="afcl-json-viewer min-w-[6rem]"
4
+ :value="value"
5
+ :expandDepth="expandDepth"
6
+ copyable
7
+ sort
8
+ :theme="currentTheme"
9
+ />
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { computed } from 'vue'
14
+ import { JsonViewer } from 'vue3-json-viewer'
15
+ import { useCoreStore } from '@/stores/core'
16
+
17
+ defineProps<{
18
+ value: any
19
+ expandDepth?: number
20
+ }>()
21
+
22
+ const coreStore = useCoreStore()
23
+
24
+ const currentTheme = computed(() => (coreStore.theme === 'dark' ? 'dark' : 'light'))
25
+ </script>
@@ -3,8 +3,8 @@
3
3
  v-bind="$attrs"
4
4
  :to="props.to"
5
5
  type="submit"
6
- class="afcl-link-button flex items-center justify-center gap-1 text-lightPrimaryContrast bg-lightPrimary dark:bg-darkPrimary hover:brightness-110
7
- focus:ring-4 focus:outline-none focus:ring-lightPrimary focus:ring-opacity-50 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:focus:ring-darkPrimary dark:focus:ring-opacity-50"
6
+ class="afcl-link-button flex items-center justify-center gap-1 text-lightButtonsText bg-lightButtonsBackground border border-lightButtonsBorder dark:bg-darkButtonsBackground hover:bg-lightButtonsHover hover:border-lightButtonsBorderHover
7
+ focus:ring-4 focus:outline-none focus:ring-lightButtonFocusRing focus:ring-opacity-50 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:focus:ring-darkButtonFocusRing dark:text-darkButtonsText dark:border-darkButtonsBorder dark:hover:bg-darkButtonsHover dark:hover:border-darkButtonsBorderHover"
8
8
  :class="{
9
9
  'cursor-default': props.disabled,
10
10
  'opacity-50': props.disabled,
@@ -18,7 +18,7 @@
18
18
  <script setup lang="ts">
19
19
 
20
20
  const props = defineProps<{
21
- disabled: boolean,
21
+ disabled?: boolean,
22
22
  to: string,
23
23
  }>();
24
24
 
@@ -63,8 +63,8 @@ const optionsBase = {
63
63
  show: false,
64
64
  fontFamily: "Inter, sans-serif",
65
65
  label: "",
66
- formatter: function (w) {
67
- const sum = w.globals.seriesTotals.reduce((a, b) => {
66
+ formatter: function (w: any) {
67
+ const sum = w.globals.seriesTotals.reduce((a: any, b: any) => {
68
68
  return a + b
69
69
  }, 0)
70
70
  return sum
@@ -74,7 +74,7 @@ const optionsBase = {
74
74
  show: true,
75
75
  fontFamily: "Inter, sans-serif",
76
76
  offsetY: -20,
77
- formatter: function (value) {
77
+ formatter: function (value: any) {
78
78
  return value + "k"
79
79
  },
80
80
  },
@@ -100,14 +100,14 @@ const optionsBase = {
100
100
  },
101
101
  yaxis: {
102
102
  labels: {
103
- formatter: function (value) {
103
+ formatter: function (value: any) {
104
104
  return value;
105
105
  },
106
106
  },
107
107
  },
108
108
  xaxis: {
109
109
  labels: {
110
- formatter: function (value) {
110
+ formatter: function (value: any) {
111
111
  return value;
112
112
  },
113
113
  },
@@ -1,14 +1,14 @@
1
1
  <template>
2
- <div class="relative mt-4 lg:mt-10 w-full max-w-[700px] bg-gray-200 rounded-full h-2.5 dark:bg-gray-700">
3
- <span class="absolute -top-6 left-0 text-sm text-gray-500">{{ leftLabel }}</span>
4
- <span class="absolute -top-6 right-0 text-sm text-gray-500">{{ rightLabel }}</span>
2
+ <div class="relative mt-4 lg:mt-10 w-full max-w-[700px] bg-lightProgressBarUnfilledColor rounded-full h-2.5 dark:bg-darkProgressBarUnfilledColor">
3
+ <span class="absolute -top-6 left-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ leftLabel }}</span>
4
+ <span class="absolute -top-6 right-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ rightLabel }}</span>
5
5
  <div
6
- class="bg-lightPrimary dark:bg-darkPrimary h-2.5 rounded-full transition-all duration-300 ease-in-out"
6
+ class="bg-lightProgressBarFilledColor dark:bg-darkProgressBarFilledColor h-2.5 rounded-full transition-all duration-300 ease-in-out"
7
7
  :style="{ width: `${percentage}%` }"
8
8
  ></div>
9
- <span v-if="showValues" class="absolute top-4 left-0 text-sm text-gray-500">{{ formatValue(minValue) }}</span>
10
- <span v-if="showProgress" class="absolute top-4 right-1/2 translate-x-1/2 text-sm text-gray-500">{{ progressText }}</span>
11
- <span v-if="showValues" class="absolute top-4 right-0 text-sm text-gray-500">{{ formatValue(maxValue) }}</span>
9
+ <span v-if="showValues" class="absolute top-4 left-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ formatValue(minValue) }}</span>
10
+ <span v-if="showProgress" class="absolute top-4 right-1/2 translate-x-1/2 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ progressText }}</span>
11
+ <span v-if="showValues" class="absolute top-4 right-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ formatValue(maxValue) }}</span>
12
12
  </div>
13
13
  </template>
14
14