@merkaly/nuxt 0.3.1 → 0.4.0

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/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.0.0"
6
6
  },
7
- "version": "0.3.1",
7
+ "version": "0.4.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
@@ -1,19 +1,25 @@
1
1
  import type { BaseButtonVariant, BaseSize } from 'bootstrap-vue-next';
2
- declare var __VLS_8: {};
2
+ import type { PropType } from 'vue';
3
+ declare var __VLS_14: {};
3
4
  type __VLS_Slots = {} & {
4
- default?: (props: typeof __VLS_8) => any;
5
+ default?: (props: typeof __VLS_14) => any;
5
6
  };
6
7
  declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
7
8
  size: {
8
9
  type: PropType<keyof BaseSize>;
9
10
  default: () => string;
10
11
  };
12
+ icon: {
13
+ type: StringConstructor;
14
+ default: () => string;
15
+ };
11
16
  text: {
12
17
  type: StringConstructor;
13
18
  default: () => string;
14
19
  };
15
20
  variant: {
16
21
  type: PropType<keyof BaseButtonVariant>;
22
+ default: () => undefined;
17
23
  };
18
24
  solid: {
19
25
  type: BooleanConstructor;
@@ -24,12 +30,17 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
24
30
  type: PropType<keyof BaseSize>;
25
31
  default: () => string;
26
32
  };
33
+ icon: {
34
+ type: StringConstructor;
35
+ default: () => string;
36
+ };
27
37
  text: {
28
38
  type: StringConstructor;
29
39
  default: () => string;
30
40
  };
31
41
  variant: {
32
42
  type: PropType<keyof BaseButtonVariant>;
43
+ default: () => undefined;
33
44
  };
34
45
  solid: {
35
46
  type: BooleanConstructor;
@@ -37,8 +48,9 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
37
48
  };
38
49
  }>> & Readonly<{}>, {
39
50
  text: string;
40
- size: any;
41
- variant: any;
51
+ size: keyof BaseSize;
52
+ variant: keyof BaseButtonVariant;
53
+ icon: string;
42
54
  solid: boolean;
43
55
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
44
56
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -1,9 +1,11 @@
1
1
  <script setup>
2
2
  import { computed, useSlots } from "vue";
3
+ import FormatIcon from "../format/FormatIcon.vue";
3
4
  const props = defineProps({
4
5
  size: { type: String, default: () => "sm" },
5
- text: { type: String, default: () => "Actions" },
6
- variant: { type: String },
6
+ icon: { type: String, default: () => "ellipsis-v" },
7
+ text: { type: String, default: () => "" },
8
+ variant: { type: String, default: () => void 0 },
7
9
  solid: { type: Boolean, default: () => false }
8
10
  });
9
11
  const slots = useSlots();
@@ -11,7 +13,16 @@ const noDefault = computed(() => !slots.default);
11
13
  </script>
12
14
 
13
15
  <template>
14
- <BDropdown :disabled="noDefault" :size="props.size" :text="props.text" :variant="props.variant" auto-close="outside">
16
+ <BDropdown
17
+ :disabled="noDefault"
18
+ :size="props.size"
19
+ :text="props.text"
20
+ :variant="props.variant"
21
+ auto-close="outside"
22
+ no-caret>
23
+ <template #button-content>
24
+ <FormatIcon :name="props.icon" :text="props.text" />
25
+ </template>
15
26
  <slot />
16
27
  </BDropdown>
17
28
  </template>
@@ -1,19 +1,25 @@
1
1
  import type { BaseButtonVariant, BaseSize } from 'bootstrap-vue-next';
2
- declare var __VLS_8: {};
2
+ import type { PropType } from 'vue';
3
+ declare var __VLS_14: {};
3
4
  type __VLS_Slots = {} & {
4
- default?: (props: typeof __VLS_8) => any;
5
+ default?: (props: typeof __VLS_14) => any;
5
6
  };
6
7
  declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
7
8
  size: {
8
9
  type: PropType<keyof BaseSize>;
9
10
  default: () => string;
10
11
  };
12
+ icon: {
13
+ type: StringConstructor;
14
+ default: () => string;
15
+ };
11
16
  text: {
12
17
  type: StringConstructor;
13
18
  default: () => string;
14
19
  };
15
20
  variant: {
16
21
  type: PropType<keyof BaseButtonVariant>;
22
+ default: () => undefined;
17
23
  };
18
24
  solid: {
19
25
  type: BooleanConstructor;
@@ -24,12 +30,17 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
24
30
  type: PropType<keyof BaseSize>;
25
31
  default: () => string;
26
32
  };
33
+ icon: {
34
+ type: StringConstructor;
35
+ default: () => string;
36
+ };
27
37
  text: {
28
38
  type: StringConstructor;
29
39
  default: () => string;
30
40
  };
31
41
  variant: {
32
42
  type: PropType<keyof BaseButtonVariant>;
43
+ default: () => undefined;
33
44
  };
34
45
  solid: {
35
46
  type: BooleanConstructor;
@@ -37,8 +48,9 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
37
48
  };
38
49
  }>> & Readonly<{}>, {
39
50
  text: string;
40
- size: any;
41
- variant: any;
51
+ size: keyof BaseSize;
52
+ variant: keyof BaseButtonVariant;
53
+ icon: string;
42
54
  solid: boolean;
43
55
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
44
56
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -32,7 +32,7 @@ const classList = computed(() => [
32
32
  <template v-if="props.text">
33
33
  <span :class="{ 'flex-row-reverse': props.reversed }" class="d-flex align-items-center">
34
34
  <component :is="props.tag" :class="classList" />
35
- <span class="ps-1" v-text="props.text" />
35
+ <span :class="fontColor" class="ps-1" v-text="props.text" />
36
36
  </span>
37
37
  </template>
38
38
 
@@ -74,10 +74,10 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
74
74
  }>> & Readonly<{
75
75
  "onUpdate:modelValue"?: ((value: Numberish) => any) | undefined;
76
76
  }>, {
77
+ min: number;
78
+ max: number;
77
79
  prefix: string;
78
80
  decimal: string;
79
- max: number;
80
- min: number;
81
81
  placeholder: string;
82
82
  precision: number;
83
83
  suffix: string;
@@ -74,10 +74,10 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
74
74
  }>> & Readonly<{
75
75
  "onUpdate:modelValue"?: ((value: Numberish) => any) | undefined;
76
76
  }>, {
77
+ min: number;
78
+ max: number;
77
79
  prefix: string;
78
80
  decimal: string;
79
- max: number;
80
- min: number;
81
81
  placeholder: string;
82
82
  precision: number;
83
83
  suffix: string;
@@ -1,3 +1,11 @@
1
- declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
1
+ declare const search: import("vue").ModelRef<string, string, string, string>;
2
+ type __VLS_ModelProps = {
3
+ modelValue?: typeof search['value'];
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
+ "update:modelValue": (value: string) => any;
7
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
8
+ "onUpdate:modelValue"?: ((value: string) => any) | undefined;
9
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
2
10
  declare const _default: typeof __VLS_export;
3
11
  export default _default;
@@ -1,10 +1,11 @@
1
1
  <script setup>
2
2
  import FormatIcon from "../format/FormatIcon.vue";
3
+ const search = defineModel({ type: String, default: () => String() });
3
4
  </script>
4
5
 
5
6
  <template>
6
7
  <div class="d-flex align-items-center position-relative">
7
- <BFormInput class="form-control-solid border" placeholder="Search..." />
8
+ <BFormInput v-model="search" class="form-control-solid border" placeholder="Search..." />
8
9
 
9
10
  <FormatIcon class="position-absolute me-3 end-0" mode="regular" name="search" size="4" variant="primary" />
10
11
  </div>
@@ -1,3 +1,11 @@
1
- declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
1
+ declare const search: import("vue").ModelRef<string, string, string, string>;
2
+ type __VLS_ModelProps = {
3
+ modelValue?: typeof search['value'];
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
+ "update:modelValue": (value: string) => any;
7
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
8
+ "onUpdate:modelValue"?: ((value: string) => any) | undefined;
9
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
2
10
  declare const _default: typeof __VLS_export;
3
11
  export default _default;
@@ -7,6 +7,10 @@ declare const __VLS_export: <G>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
7
7
  type: StringConstructor;
8
8
  default: () => string;
9
9
  };
10
+ hideHeader: {
11
+ type: BooleanConstructor;
12
+ default: boolean;
13
+ };
10
14
  hideFooter: {
11
15
  type: BooleanConstructor;
12
16
  default: boolean;
@@ -40,6 +44,11 @@ declare const __VLS_export: <G>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
40
44
  item: G;
41
45
  key: string;
42
46
  }) => any) | undefined;
47
+ } & {
48
+ bulk?: (props: {
49
+ items: G[];
50
+ count: number;
51
+ }) => any;
43
52
  } & {
44
53
  search?: (props: {}) => any;
45
54
  } & {
@@ -9,6 +9,7 @@ const emit = defineEmits(["fetch", "toggle:item"]);
9
9
  const slots = useSlots();
10
10
  const props = defineProps({
11
11
  emptyText: { type: String, default: () => "No items found" },
12
+ hideHeader: { type: Boolean, default: false },
12
13
  hideFooter: { type: Boolean, default: false },
13
14
  hidePagination: { type: Boolean, default: false },
14
15
  hideSelect: { type: Boolean, default: false }
@@ -17,8 +18,9 @@ const $datagrid = defineModel({ type: Object, ...{ type: Object, required: true
17
18
  const instance = getCurrentInstance();
18
19
  const canFetch = Boolean(instance?.vnode?.props?.onFetch);
19
20
  const canFilter = Boolean(slots.filters);
20
- const hasDetails = computed(() => Boolean(slots["details"]));
21
- const hasActions = computed(() => Boolean(slots["actions"]));
21
+ const hasDetailsSlot = computed(() => Boolean(slots["details"]));
22
+ const hasActionsSlot = computed(() => Boolean(slots["actions"]));
23
+ const hasBulkSlot = computed(() => Boolean(slots["bulk"]));
22
24
  const visibleColumns = computed(() => Object.entries($datagrid.value.columns));
23
25
  const tableColspan = computed(() => {
24
26
  const baseColumns = 2;
@@ -50,9 +52,10 @@ const visibleItems = computed(() => {
50
52
  const start = Math.max(0, paginationText.value.values.current - 1);
51
53
  return $datagrid.value.items.slice(start, paginationText.value.values.final);
52
54
  });
55
+ const selectedItems = computed(() => $datagrid.value.items.filter((it) => it._checked));
53
56
  const checkboxAllAttrs = computed(() => {
54
57
  const attrs = {};
55
- const values = $datagrid.value.items.map((it) => it._checked);
58
+ const values = selectedItems.value.map((it) => it._checked);
56
59
  const allChecked = values.length > 0 && values.every(Boolean);
57
60
  const someChecked = values.some(Boolean);
58
61
  attrs["checked"] = allChecked;
@@ -71,14 +74,28 @@ function toggleDetails(item) {
71
74
  record._showDetails = !record._showDetails;
72
75
  emit("toggle:item", item, Boolean(record._showDetails));
73
76
  }
77
+ function emitSearch() {
78
+ $datagrid.value.page = 1;
79
+ emit("fetch", "search");
80
+ }
74
81
  </script>
75
82
 
76
83
  <template>
77
84
  <BCard no-body>
78
- <BCardHeader class="align-items-center p-4">
85
+ <BCardHeader v-if="!props.hideHeader" class="align-items-center p-4">
79
86
  <BCardTitle>
87
+ <div v-if="hasBulkSlot && !props.hideSelect && selectedItems.length" class="w-35px">
88
+ <DropdownIcon icon="caret-down" size="sm" toggle-class="p-2">
89
+ <slot name="bulk" v-bind="{ items: selectedItems, count: selectedItems.length }" />
90
+ </DropdownIcon>
91
+ </div>
92
+
80
93
  <slot name="search">
81
- <InputSearch :disabled="$datagrid.loading" class="w-250px" />
94
+ <InputSearch
95
+ v-model="$datagrid.search"
96
+ :disabled="$datagrid.loading"
97
+ class="max-w-250px"
98
+ @change="emitSearch()" />
82
99
  </slot>
83
100
  </BCardTitle>
84
101
 
@@ -133,14 +150,14 @@ function toggleDetails(item) {
133
150
 
134
151
  <BTableSimple
135
152
  :class="{ 'h-100': !visibleItems.length || $datagrid.loading }"
136
- class="mb-0"
153
+ class="mb-0 overflow-auto"
137
154
  hover
138
155
  responsive="lg"
139
156
  small
140
157
  table-class="align-middle table-row-dashed gy-3 h-100">
141
158
  <BThead class="sticky-top z-index-1">
142
159
  <BTr class="text-start text-body-secondary fw-bold fs-7 text-uppercase gs-0">
143
- <BTh v-if="hasDetails" class="p-0 w-25px" />
160
+ <BTh v-if="hasDetailsSlot" class="p-0 w-25px" />
144
161
 
145
162
  <BTh v-if="!props.hideSelect" class="w-40px px-0">
146
163
  <div class="form-check form-check-sm form-check-custom cell-checkbox">
@@ -161,7 +178,7 @@ function toggleDetails(item) {
161
178
  </BTh>
162
179
  </template>
163
180
 
164
- <BTh v-if="hasActions" class="text-end px-3" />
181
+ <BTh v-if="hasActionsSlot" class="text-end px-3" />
165
182
  </BTr>
166
183
  </BThead>
167
184
 
@@ -184,7 +201,7 @@ function toggleDetails(item) {
184
201
  <BTbody v-else class="fw-semibold text-gray-600">
185
202
  <template v-for="(item, idx) in visibleItems" :key="idx">
186
203
  <BTr>
187
- <BTd v-if="hasDetails" class="p-0 w-25px">
204
+ <BTd v-if="hasDetailsSlot" class="p-0 w-25px">
188
205
  <BButton
189
206
  class="w-25px h-100 rounded-0 p-0 bg-light bg-hover-light-secondary border-end border-dashed"
190
207
  size="sm"
@@ -208,14 +225,14 @@ function toggleDetails(item) {
208
225
  </BTd>
209
226
  </template>
210
227
 
211
- <BTd v-if="hasActions" class="text-end px-3">
212
- <DropdownIcon toggle-class="border border-secondary-subtle border-dashed text-body">
228
+ <BTd v-if="hasActionsSlot" class="text-end px-3">
229
+ <DropdownIcon toggle-class="border border-secondary-subtle border-dashed text-body py-1 px-3">
213
230
  <slot :index="idx" :item="item" name="actions" />
214
231
  </DropdownIcon>
215
232
  </BTd>
216
233
  </BTr>
217
234
 
218
- <BTr v-if="hasDetails && item._showDetails">
235
+ <BTr v-if="hasDetailsSlot && item._showDetails">
219
236
  <BTd :colspan="tableColspan" class="p-0">
220
237
  <slot
221
238
  :index="idx"
@@ -7,6 +7,10 @@ declare const __VLS_export: <G>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
7
7
  type: StringConstructor;
8
8
  default: () => string;
9
9
  };
10
+ hideHeader: {
11
+ type: BooleanConstructor;
12
+ default: boolean;
13
+ };
10
14
  hideFooter: {
11
15
  type: BooleanConstructor;
12
16
  default: boolean;
@@ -40,6 +44,11 @@ declare const __VLS_export: <G>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
40
44
  item: G;
41
45
  key: string;
42
46
  }) => any) | undefined;
47
+ } & {
48
+ bulk?: (props: {
49
+ items: G[];
50
+ count: number;
51
+ }) => any;
43
52
  } & {
44
53
  search?: (props: {}) => any;
45
54
  } & {
@@ -14,6 +14,7 @@ export interface DataGrid<C = unknown> {
14
14
  limit: number;
15
15
  loading: boolean;
16
16
  page: number;
17
+ search: string;
17
18
  total: number;
18
19
  fn: {
19
20
  addItem: (item: C) => DataGridItem<C>[];
@@ -26,6 +27,7 @@ interface OptionArgs<D> {
26
27
  items?: D[];
27
28
  limit?: number;
28
29
  loading?: boolean;
30
+ search?: string;
29
31
  total?: number;
30
32
  }
31
33
  export declare function useDatagrid<D = unknown>(params: OptionArgs<D>): DataGrid<D>;
@@ -4,6 +4,7 @@ export function useDatagrid(params) {
4
4
  columns: params.columns,
5
5
  error: params.error ?? null,
6
6
  items: params.items ?? [],
7
+ search: params.search,
7
8
  limit: params.limit ?? 10,
8
9
  loading: params.loading ?? false,
9
10
  page: 1,
@@ -1,20 +1,13 @@
1
1
  interface NavigationItem {
2
- text: string | null;
3
- path: string;
2
+ disabled?: boolean;
4
3
  loading?: boolean;
4
+ path: string;
5
+ text: string | null;
5
6
  }
6
7
  type NavigationItemOrGetter = NavigationItem | (() => NavigationItem);
7
8
  export declare function useNavigation(page?: NavigationItemOrGetter): {
8
- current: import("vue").ComputedRef<{
9
- path: string;
10
- text: string | null;
11
- loading: boolean;
12
- } | undefined>;
13
- items: import("vue").ComputedRef<{
14
- path: string;
15
- text: string | null;
16
- loading: boolean;
17
- }[]>;
9
+ current: import("vue").ComputedRef<NavigationItem | undefined>;
10
+ items: import("vue").ComputedRef<NavigationItem[]>;
18
11
  defer: (route: {
19
12
  path: string;
20
13
  }) => void;
@@ -3,49 +3,54 @@ import { useState } from "#imports";
3
3
  export function useNavigation(page) {
4
4
  const list = useState("breadcrumbs", () => []);
5
5
  const pendingRoute = useState("breadcrumbs:pending", () => null);
6
- function normalizePath(base, path) {
7
- return `${base.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
8
- }
9
- function resolve(item) {
10
- return typeof item === "function" ? item() : item;
11
- }
6
+ const resolveItem = (item) => typeof item === "function" ? item() : item;
7
+ const normalizePath = (base, path) => `${base.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
8
+ const isVisibleItem = (item) => item.text != null && !item.loading;
12
9
  const resolved = computed(() => {
13
10
  let uri = "";
14
- return list.value.map((item) => {
15
- const { text, path = "", loading } = resolve(item);
16
- uri = normalizePath(uri, path);
17
- return { path: uri, text, loading: !!loading };
11
+ return list.value.map((rawItem) => {
12
+ const item = resolveItem(rawItem);
13
+ uri = normalizePath(uri, item.path ?? "");
14
+ return {
15
+ disabled: item.disabled,
16
+ loading: !!item.loading,
17
+ path: uri,
18
+ text: item.text
19
+ };
18
20
  });
19
21
  });
20
- const items = computed(() => {
21
- return resolved.value.filter(({ text, loading }) => text != null && !loading);
22
- });
23
- const current = computed(() => {
24
- return resolved.value.at(-1);
25
- });
22
+ const items = computed(() => resolved.value.filter(isVisibleItem));
23
+ const current = computed(() => resolved.value.at(-1));
26
24
  function regenerate() {
27
25
  if (!pendingRoute.value) return;
28
26
  const route = pendingRoute.value;
29
27
  pendingRoute.value = null;
30
- const itemIndex = resolved.value.findLastIndex((value) => route.startsWith(value.path));
31
- if (itemIndex >= 0) {
32
- list.value = list.value.slice(0, itemIndex + 1);
33
- return;
34
- }
35
- list.value = [];
36
- }
37
- if (page) {
38
- regenerate();
39
- const { path } = resolve(page);
40
- const existingIndex = list.value.findIndex((i) => resolve(i).path === path);
41
- if (existingIndex >= 0) {
42
- list.value[existingIndex] = page;
43
- } else {
44
- list.value.push(page);
45
- }
28
+ const index = findLastMatchingIndex(route);
29
+ list.value = index >= 0 ? list.value.slice(0, index + 1) : [];
46
30
  }
47
31
  function defer(route) {
48
32
  pendingRoute.value = route.path;
49
33
  }
34
+ function findLastMatchingIndex(route) {
35
+ return resolved.value.findLastIndex(
36
+ (item) => route.startsWith(item.path)
37
+ );
38
+ }
39
+ function upsertPage(page2) {
40
+ const { path } = resolveItem(page2);
41
+ const index = list.value.findIndex(
42
+ (item) => resolveItem(item).path === path
43
+ );
44
+ if (index >= 0) {
45
+ list.value[index] = page2;
46
+ return;
47
+ }
48
+ list.value.push(page2);
49
+ }
50
+ if (!page) {
51
+ return { current, items, defer, regenerate };
52
+ }
53
+ regenerate();
54
+ upsertPage(page);
50
55
  return { current, items, defer, regenerate };
51
56
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@merkaly/nuxt",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/merkaly-io/nuxt.git"
@@ -42,9 +42,9 @@
42
42
  "test:watch": "vitest watch"
43
43
  },
44
44
  "dependencies": {
45
- "@auth0/auth0-spa-js": "^2.14.0",
45
+ "@auth0/auth0-spa-js": "^2.15.0",
46
46
  "@bootstrap-vue-next/nuxt": "^0.43.0",
47
- "@nuxt/devtools": "^3.1.0",
47
+ "@nuxt/devtools": "^3.2.1",
48
48
  "@nuxt/eslint": "1.13.0",
49
49
  "@nuxt/eslint-config": "^1.13.0",
50
50
  "@nuxt/fonts": "0.13.0",
@@ -60,6 +60,7 @@
60
60
  "bootstrap": "^5.3.8",
61
61
  "bootstrap-vue-next": "^0.43.0",
62
62
  "change-case": "^5",
63
+ "class-transformer": "^0.5.1",
63
64
  "class-validator": "^0.14.3",
64
65
  "eslint": "^9.39.1",
65
66
  "filesize": "^11.0.2",