@paris-ias/list 1.0.109 → 1.0.112

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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@paris-ias/list",
3
3
  "configKey": "list",
4
- "version": "1.0.109",
4
+ "version": "1.0.112",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.0",
7
7
  "unbuild": "3.5.0"
@@ -2,10 +2,19 @@
2
2
  <v-row
3
3
  v-ripple
4
4
  no-gutters
5
- class="cursor-pointer highlight-on-hover"
5
+ class="cursor-pointer highlight-on-hover my-2"
6
6
  @click="$router.push(localePath('/activities/events/' + item.slug[locale]))"
7
7
  >
8
- <v-col v-if="mdAndUp" align-self="center" cols="1">
8
+ <v-col v-if="mdAndUp" cols="1">
9
+ <MiscAtomsDateStamp
10
+ v-if="item.start"
11
+ :loading="$stores['events'].loading"
12
+ :date-start="item.start"
13
+ :date-stop="item.stop"
14
+ class="pr-6 mt-md-2"
15
+ />
16
+ </v-col>
17
+ <v-col v-if="mdAndUp" cols="1">
9
18
  <MiscAtomsImageContainer
10
19
  cover
11
20
  :loading="$stores.events.loading"
@@ -18,19 +27,19 @@
18
27
  />
19
28
  </v-col>
20
29
 
21
- <v-col align-self="center" class="pl-2">
22
- <div class="text-h5 dense text-overline font-weight-black">
23
- {{
24
- $t("list.filters.events.category." + item.category) +
25
- " / " +
26
- new Date(item.start).toLocaleDateString(locale, {
27
- year: "numeric",
28
- month: "numeric",
29
- day: "numeric",
30
- })
31
- }}
32
- </div>
33
- <div class="text-h5 dense">
30
+ <v-col align-self="top" class="pl-2">
31
+ <v-chip
32
+ class="mr-3"
33
+ color="black"
34
+ :size="mdAndUp ? 'default' : 'small'"
35
+ style="background-color: white; color: black"
36
+ tile
37
+ variant="outlined"
38
+ >
39
+ {{ $t("list.filters.events.category." + item.category) }}
40
+ </v-chip>
41
+
42
+ <div class="text-h5 dense paragraph">
34
43
  {{ item.name }}
35
44
  </div>
36
45
  </v-col>
@@ -4,7 +4,7 @@
4
4
  no-gutters
5
5
  class="cursor-pointer highlight-on-hover"
6
6
  @click="
7
- $router.push(localePath('/activities/fellowships' + item.slug[locale]))
7
+ $router.push(localePath('/activities/fellowships/' + item.slug[locale]))
8
8
  "
9
9
  >
10
10
  <v-col align-self="center" cols="8" class="text-h5 dense">
@@ -0,0 +1,39 @@
1
+ <template>
2
+ <v-select
3
+ v-model="itemsPerPage"
4
+ class="perPageSelect"
5
+ density="compact"
6
+ variant="outlined"
7
+ :items="itemsPerPageArray"
8
+ hide-details
9
+ :loading="$rootStore.loading"
10
+ @update:model-value="
11
+ $rootStore.updateItemsPerPage({
12
+ value: $event,
13
+ type: type,
14
+ lang: locale,
15
+ })
16
+ "
17
+ />
18
+ </template>
19
+
20
+ <script setup>
21
+ import { useNuxtApp, useI18n } from "#imports";
22
+ import { useDisplay } from "vuetify";
23
+ import { computed, ref } from "vue";
24
+ const { smAndDown } = useDisplay();
25
+ const { $stores, $rootStore } = useNuxtApp();
26
+ const { locale } = useI18n();
27
+ const props = defineProps({
28
+ type: {
29
+ type: String,
30
+ required: false,
31
+ default: ""
32
+ }
33
+ });
34
+ const itemsPerPage = ref($stores[props.type].itemsPerPage);
35
+ const itemsPerPageArray = computed(
36
+ () => $stores[props.type].view.perPage.options
37
+ );
38
+ const scrolled = computed(() => $stores.scrolled);
39
+ </script>
@@ -3,6 +3,7 @@
3
3
  <v-text-field
4
4
  v-model.trim="search"
5
5
  :placeholder="$t('list.search-type', [$t('items.' + type, 2)])"
6
+ :append-icon="type === 'all' ? 'mdi-magnify' : false"
6
7
  prepend-inner-icon="mdi-magnify"
7
8
  single-line
8
9
  class="transition-swing"
@@ -12,6 +13,10 @@
12
13
  tile
13
14
  type="search"
14
15
  :loading="rootStore.loading"
16
+ @keyup.enter="type === 'all' ? null : $router.push(localePath('/search'))"
17
+ @click:append="
18
+ type === 'all' ? null : $router.push(localePath('/search'))
19
+ "
15
20
  >
16
21
  <!-- :loading="$nuxt.loading || $store.state.loading" :class="{ 'mt-3':
17
22
  $store.state.scrolled }" -->
@@ -32,6 +37,8 @@
32
37
  import { useDebounceFn } from "@vueuse/core";
33
38
  import { useRootStore } from "../../../stores/root";
34
39
  import { computed, useI18n } from "#imports";
40
+ const { $router } = useNuxtApp();
41
+ const localePath = useLocalePath();
35
42
  const { locale } = useI18n();
36
43
  const rootStore = useRootStore();
37
44
  const props = defineProps({
@@ -3,7 +3,11 @@
3
3
  <div class="d-flex align-center">
4
4
  <v-text-field
5
5
  v-model.trim="search"
6
- :placeholder="$t('list.search-type', [$t('items.' + type, 2)])"
6
+ :placeholder="
7
+ type === 'all'
8
+ ? t('search')
9
+ : $t('list.search-type', [$t('items.' + type, 2)])
10
+ "
7
11
  prepend-inner-icon="mdi-magnify"
8
12
  single-line
9
13
  class="transition-swing flex-grow-1"
@@ -13,6 +17,7 @@
13
17
  tile
14
18
  type="search"
15
19
  :loading="rootStore.loading"
20
+ @keyup.enter="$router.push(localePath('/search'))"
16
21
  >
17
22
  <!-- :loading="$nuxt.loading || $store.state.loading" :class="{ 'mt-3':
18
23
  $store.state.scrolled }" -->
@@ -37,7 +42,7 @@
37
42
  <v-btn
38
43
  v-bind="menuProps"
39
44
  :rounded="0"
40
- variant="outlined"
45
+ variant="text"
41
46
  size="large"
42
47
  height="56"
43
48
  >
@@ -45,6 +50,9 @@
45
50
  <v-icon class="ml-1" size="small">
46
51
  {{ filterMenuOpen ? "mdi-chevron-up" : "mdi-chevron-down" }}
47
52
  </v-icon>
53
+ <v-tooltip activator="parent" location="start">
54
+ {{ $t("filter-by-type") }}
55
+ </v-tooltip>
48
56
  </v-btn>
49
57
  </template>
50
58
 
@@ -67,6 +75,21 @@
67
75
  </v-list>
68
76
  </v-card>
69
77
  </v-menu>
78
+ <v-btn
79
+ v-bind="attrs"
80
+ :rounded="0"
81
+ variant="test"
82
+ size="large"
83
+ height="56"
84
+ v-on="on"
85
+ @keyup.enter="$router.push(localePath('/search'))"
86
+ @click="$router.push(localePath('/search'))"
87
+ >
88
+ <v-icon>mdi-magnify</v-icon>
89
+ <v-tooltip activator="parent" location="start">{{
90
+ $t("click-here-to-search")
91
+ }}</v-tooltip>
92
+ </v-btn>
70
93
  </div>
71
94
  </div>
72
95
  </template>
@@ -94,14 +117,14 @@ const props = defineProps({
94
117
  }
95
118
  });
96
119
  const filterMenuOpen = ref(false);
97
- const filterOptions = [
120
+ const filterOptions = computed(() => [
98
121
  { value: "people", label: capitalize(t("items.people", 2)) },
99
122
  { value: "events", label: capitalize(t("items.events", 2)) },
100
123
  { value: "news", label: capitalize(t("items.news", 2)) },
101
124
  { value: "publications", label: capitalize(t("items.publications", 2)) },
102
125
  { value: "fellowships", label: capitalize(t("items.fellowships", 2)) },
103
126
  { value: "projects", label: capitalize(t("items.projects", 2)) }
104
- ];
127
+ ]);
105
128
  const toggleFilter = (option) => {
106
129
  const currentCategories = [...props.categories];
107
130
  const index = currentCategories.indexOf(option.value);
@@ -16,6 +16,7 @@
16
16
  <ListMoleculesFilters :type="type" />
17
17
  </div>
18
18
  </v-expand-transition>
19
+
19
20
  <ListAtomsSearchInput :type="type" />
20
21
  <ListAtomsSearchString :type="type" />
21
22
  </v-col>
@@ -15,7 +15,12 @@
15
15
  class="d-flex flex-column cursor-pointer"
16
16
  @click="$emit('toggle', type)"
17
17
  >
18
- <div class="text-h4">
18
+ <div
19
+ class="text-h4"
20
+ :class="
21
+ $rootStore.results[type].total > 0 ? '' : 'text-grey darken-2'
22
+ "
23
+ >
19
24
  {{ capitalize($t("items." + props.type, 2)) }}
20
25
  </div>
21
26
  <div class="text-overline">
@@ -58,7 +63,9 @@
58
63
  <script setup>
59
64
  import { useNuxtApp, useLocalePath } from "#imports";
60
65
  const localePath = useLocalePath();
66
+ const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
61
67
  const { $rootStore } = useNuxtApp();
68
+ defineEmits(["toggle"]);
62
69
  const props = defineProps({
63
70
  type: {
64
71
  type: String,
@@ -10,6 +10,12 @@
10
10
  />
11
11
  </component>
12
12
  <div class="text-center">
13
+ <ListAtomsPerPage
14
+ v-if="numberOfPages > 1"
15
+ :type="type"
16
+ class="float-right"
17
+ />
18
+
13
19
  <ListMoleculesPagination
14
20
  v-if="numberOfPages > 1"
15
21
  :type="type"
@@ -3,17 +3,15 @@
3
3
  type="all"
4
4
  :placeholder="$t('search')"
5
5
  variant="outlined"
6
+ :categories="selectedCategories"
7
+ @filter-change="handleFilterChange"
6
8
  />
7
9
  <ListMoleculesResultsContainer
8
- v-for="type in sortedModules"
10
+ v-for="type in filteredSortedModules"
9
11
  :key="type"
10
12
  :feminine="type === 'people'"
11
13
  :type
12
- :open="
13
- open[type] !== undefined
14
- ? open[type]
15
- : $rootStore.results[type]?.total > 0
16
- "
14
+ :open="$rootStore.results[type]?.total > 0 || open[type]"
17
15
  @toggle="open[$event] = !open[$event]"
18
16
  >
19
17
  <v-expand-transition class="results-container">
@@ -27,7 +25,6 @@
27
25
  <script setup>
28
26
  import {
29
27
  useNuxtApp,
30
- useLocalePath,
31
28
  onBeforeUnmount,
32
29
  onMounted,
33
30
  useI18n,
@@ -35,7 +32,6 @@ import {
35
32
  ref,
36
33
  computed
37
34
  } from "#imports";
38
- const localePath = useLocalePath();
39
35
  defineOptions({
40
36
  name: "SearchResults"
41
37
  });
@@ -43,6 +39,10 @@ const { $rootStore } = useNuxtApp();
43
39
  const appConfig = useAppConfig();
44
40
  const { locale } = useI18n();
45
41
  const open = ref({});
42
+ const selectedCategories = ref([...appConfig.list.modules]);
43
+ const handleFilterChange = (filterData) => {
44
+ selectedCategories.value = filterData.categories;
45
+ };
46
46
  const sortedModules = computed(() => {
47
47
  return appConfig.list.modules.slice().sort((a, b) => {
48
48
  const aResults = $rootStore.results[a] || { total: 0 };
@@ -50,6 +50,11 @@ const sortedModules = computed(() => {
50
50
  return (bResults.total || 0) - (aResults.total || 0);
51
51
  });
52
52
  });
53
+ const filteredSortedModules = computed(() => {
54
+ return sortedModules.value.filter(
55
+ (type) => selectedCategories.value.includes(type)
56
+ );
57
+ });
53
58
  onMounted(async () => {
54
59
  console.log("mounted list");
55
60
  try {
@@ -2,7 +2,6 @@
2
2
  <section class="my-6 ml-2 pb-6">
3
3
  <slot>
4
4
  <!-- fallback content -->
5
- Dense view
6
5
  </slot>
7
6
  </section>
8
7
  </template>
@@ -2,43 +2,44 @@
2
2
  <v-row
3
3
  v-ripple
4
4
  no-gutters
5
- class="cursor-pointer highlight-on-hover"
5
+ class="cursor-pointer highlight-on-hover my-2"
6
6
  @click="$router.push(localePath('/activities/news/' + item.slug[locale]))"
7
7
  >
8
8
  <v-col v-if="mdAndUp" align-self="center" cols="1">
9
9
  <MiscAtomsImageContainer
10
10
  cover
11
- :loading="$stores.people.loading"
11
+ :loading="$stores.news.loading"
12
12
  :src="item.image.url ? item.image : '/default.png'"
13
13
  :ratio="1 / 1"
14
- :name="item.lastname + ' ' + item.firstname"
14
+ :name="item.slug"
15
15
  :slug="item.slug"
16
- link="people-slug"
16
+ link="news-slug"
17
17
  width="80px"
18
18
  />
19
19
  </v-col>
20
- <v-col align-self="center" class="text-h5 dense pl-2">
20
+ <v-col align-self="top" class="text-h5 dense px-2 paragraph">
21
21
  <v-skeleton-loader v-if="rootStore.loading" type="heading" />
22
22
  <template v-else>
23
- {{ item.name }}
24
- </template>
25
- </v-col>
23
+ <v-skeleton-loader
24
+ v-if="rootStore.loading"
25
+ :type="
26
+ ['chip', 'chip@2', 'chip@3', 'chip@4', 'chip@4', 'chip@4'][
27
+ ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'].indexOf(name || 'md')
28
+ ]
29
+ "
30
+ />
26
31
 
27
- <v-col align-self="center">
28
- <v-skeleton-loader
29
- v-if="rootStore.loading"
30
- :type="
31
- ['chip', 'chip@2', 'chip@3', 'chip@4', 'chip@4', 'chip@4'][
32
- ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'].indexOf(name || 'md')
33
- ]
34
- "
35
- />
36
-
37
- <template v-else>
38
- <v-chip class="ma-2" style="background-color: white; color: black">
39
- {{ $t(eventCategory) }}
40
- </v-chip>
41
- <MiscMoleculesChipContainer :items="item.tags" size="small" />
32
+ <template v-else>
33
+ <v-chip
34
+ class="ma-2"
35
+ style="background-color: white; color: black"
36
+ :size="mdAndUp ? 'default' : 'small'"
37
+ >
38
+ {{ $t(eventCategory) }}
39
+ </v-chip>
40
+ <MiscMoleculesChipContainer :items="item.tags" size="small" />
41
+ </template>
42
+ {{ item.name }}
42
43
  </template>
43
44
  </v-col>
44
45
  </v-row>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div>
2
+ <span>
3
3
  <v-skeleton-loader
4
4
  v-if="rootStore.loading || $stores.people.loading"
5
5
  type="chip"
@@ -10,6 +10,7 @@
10
10
  <v-chip
11
11
  v-for="(vintage, index2) in item.groups.vintage"
12
12
  :key="index2"
13
+ :size="mdAndUp ? 'default' : 'small'"
13
14
  class="mt-3 mr-3"
14
15
  variant="outlined"
15
16
  tile
@@ -27,6 +28,7 @@
27
28
  !['vintage', '__typename'].includes(key))
28
29
  "
29
30
  class="mt-3 mr-3"
31
+ :size="mdAndUp ? 'default' : 'small'"
30
32
  color="black"
31
33
  style="background-color: white; color: black"
32
34
  tile
@@ -35,13 +37,31 @@
35
37
  {{ $t("list.filters.people.groups." + key) }}
36
38
  </v-chip>
37
39
  </template>
38
- </template>
39
- </div>
40
+ <template
41
+ v-for="(value, key, index) in item.disciplines"
42
+ :key="key + index"
43
+ >
44
+ <v-chip
45
+ v-if="value && index < 3"
46
+ class="mt-3 mr-3"
47
+ color="black"
48
+ :size="mdAndUp ? 'default' : 'small'"
49
+ style="background-color: white; color: black"
50
+ tile
51
+ variant="outlined"
52
+ >
53
+ {{ $t("disciplines." + key) }}
54
+ </v-chip>
55
+ </template>
56
+ </template></span
57
+ >
40
58
  </template>
41
59
 
42
60
  <script setup>
43
61
  import { useRootStore } from "../../stores/root";
44
62
  import { useNuxtApp } from "#imports";
63
+ import { useDisplay } from "vuetify";
64
+ const { mdAndUp } = useDisplay();
45
65
  const rootStore = useRootStore();
46
66
  const { $stores } = useNuxtApp();
47
67
  const props = defineProps({
@@ -2,7 +2,7 @@
2
2
  <v-row
3
3
  v-ripple
4
4
  no-gutters
5
- class="cursor-pointer highlight-on-hover"
5
+ class="cursor-pointer highlight-on-hover my-2"
6
6
  @click="
7
7
  $router.push(
8
8
  localePath({
@@ -12,7 +12,7 @@
12
12
  )
13
13
  "
14
14
  >
15
- <v-col v-if="mdAndUp" align-self="center" cols="1">
15
+ <v-col v-if="mdAndUp" cols="1">
16
16
  <MiscAtomsImageContainer
17
17
  cover
18
18
  :loading="$stores.people.loading"
@@ -24,18 +24,17 @@
24
24
  width="80px"
25
25
  />
26
26
  </v-col>
27
- <v-col align-self="center" class="text-h6 dense pl-2">
27
+ <v-col align-self="top" class="text-h6 dense px-2">
28
28
  <v-skeleton-loader v-if="rootStore.loading" type="heading" />
29
- <div v-else class="text-h5">
29
+ <div v-else class="d-flex text-h5 align-center">
30
30
  {{ item.firstname + " " + item.lastname }}
31
+ <v-spacer />
32
+ <PeopleBadges :item="item" />
31
33
  </div>
32
- <div class="mt-2 text-body-2 font-weight-light">
34
+ <div class="text-body-1 font-weight-light paragraph">
33
35
  {{ item.groups.vintage ? item.groups.vintage[0].theme : "" }}
34
36
  </div>
35
37
  </v-col>
36
- <v-col align-self="center" cols="auto">
37
- <PeopleGroupBadges :item="item" />
38
- </v-col>
39
38
  </v-row>
40
39
  </template>
41
40
 
@@ -58,3 +57,7 @@ const props = defineProps({
58
57
  }
59
58
  });
60
59
  </script>
60
+
61
+ <style>
62
+ .paragraph{max-width:83ch!important}
63
+ </style>
@@ -7,7 +7,7 @@
7
7
  localePath({
8
8
  name: 'people-slug',
9
9
  params: { slug: item.slug },
10
- })
10
+ }),
11
11
  )
12
12
  "
13
13
  >
@@ -51,7 +51,7 @@
51
51
  {{ item.firstname + " " + item.lastname }}
52
52
  </NuxtLink>
53
53
  <MiscAtomsSocials v-if="item.socials" :socials="item.socials" />
54
- <PeopleGroupBadges :item="item" />
54
+ <PeoplepBadges :item="item" />
55
55
  <div
56
56
  v-if="item.biography && item.biography.length > 0"
57
57
  class="text-wrap clamped-text text-black"
@@ -56,7 +56,7 @@
56
56
  </div>
57
57
  <!-- GROUPS -->
58
58
  <div class="mt-6 align-self-center">
59
- <PeopleGroupBadges v-if="item && item.groups" :item="item" />
59
+ <PeopleBadges v-if="item && item.groups" :item="item" />
60
60
  </div>
61
61
  </template>
62
62
  </v-col>
@@ -23,10 +23,8 @@
23
23
  <v-skeleton-loader v-if="rootStore.loading" type="heading" />
24
24
  <template v-else>
25
25
  {{ item.name }}
26
- <div class="text-body-2">
27
- {{ item.summary }}
28
- </div>
29
26
  </template>
27
+ <MDC class="text-caption" :value="item.summary" />
30
28
  </v-col>
31
29
 
32
30
  <v-col align-self="center">
@@ -40,9 +38,18 @@
40
38
  />
41
39
 
42
40
  <template v-else>
43
- <v-chip class="ma-2" style="background-color: white; color: black">
41
+ <v-chip
42
+ v-if="eventCategory"
43
+ class="ma-2"
44
+ style="background-color: white; color: black"
45
+ >
44
46
  {{ $t(eventCategory) }} </v-chip
45
- ><v-chip class="ma-2" style="background-color: white; color: black">
47
+ ><v-chip
48
+ v-if="eventType"
49
+ c
50
+ class="ma-2"
51
+ style="background-color: white; color: black"
52
+ >
46
53
  {{ $t(eventType) }}
47
54
  </v-chip>
48
55
  <MiscMoleculesChipContainer :items="item.tags" size="small" />
@@ -75,7 +82,7 @@ const eventCategory = computed(() => {
75
82
  console.log("props.item.category: ", props.item.category);
76
83
  return "list.filters.publications.category." + props.item.category;
77
84
  } else {
78
- return "list.filters.publications.category.OTHERS";
85
+ return false;
79
86
  }
80
87
  });
81
88
  const eventType = computed(() => {
@@ -83,7 +90,7 @@ const eventType = computed(() => {
83
90
  console.log("props.item.type: ", props.item.type);
84
91
  return "list.filters.publications.type." + props.item.type;
85
92
  } else {
86
- return "list.filters.publications.type.OTHERS";
93
+ return false;
87
94
  }
88
95
  });
89
96
  </script>
@@ -7,6 +7,8 @@ query listPublications(
7
7
  items {
8
8
  date
9
9
  id
10
+ eventCategory
11
+ type
10
12
  image {
11
13
  alt
12
14
  backgroundColor
@@ -218,7 +218,7 @@
218
218
  "disciplines": {
219
219
  "label": "Disciplines"
220
220
  },
221
- "eventCategories": {
221
+ "category": {
222
222
  "label": "Event category",
223
223
  "COLLOQUIUM": "Colloquium",
224
224
  "CONFERENCE": "Conference",
@@ -346,5 +346,6 @@
346
346
  "list.by-vintage-from-recent-to-old": "By Year, from recent to old",
347
347
  "list.by-vintage-from-old-to-recent": "By Year, from old to recent",
348
348
  "no-biography": "No biography available",
349
- "search": "Search"
349
+ "search": "Search",
350
+ "items-per-page": "Items per page"
350
351
  }
@@ -342,8 +342,9 @@
342
342
  "visit-this-project-website": "Visitez le site Web du projet",
343
343
  "visit-this-publications-website": "Visitez la page Web de cette publication",
344
344
  "no-biography": "Aucune biographie disponible",
345
- "search": "Rechercher",
346
345
  "close-the-filter-panel": "Réduire les filtres",
347
346
  "list.by-vintage-from-recent-to-old": "Par année, du plus récent au plus vieux",
348
- "list.by-vintage-from-old-to-recent": "Par année, du plus vieux au plus récent"
347
+ "list.by-vintage-from-old-to-recent": "Par année, du plus vieux au plus récent",
348
+ "search": "Rechercher",
349
+ "items-per-page": "Éléments par page"
349
350
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "license": "AGPL-3.0-only",
3
3
  "main": "./dist/module.mjs",
4
- "version": "1.0.109",
4
+ "version": "1.0.112",
5
5
  "name": "@paris-ias/list",
6
6
  "repository": {
7
7
  "url": "git+https://github.com/IEA-Paris/list.git",
@@ -24,7 +24,7 @@
24
24
  "@nuxtjs/apollo": "^5.0.0-alpha.14",
25
25
  "@nuxtjs/i18n": "^9.5.2",
26
26
  "@nuxtjs/mdc": "0.16.1",
27
- "@paris-ias/data": "^1.8.27",
27
+ "@paris-ias/data": "^1.8.31",
28
28
  "@pinia/nuxt": "^0.5.4",
29
29
  "@types/node": "latest",
30
30
  "@urql/exchange-execute": "2.3.1",