@paris-ias/list 1.0.179 → 1.0.181

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.179",
4
+ "version": "1.0.181",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
@@ -1,6 +1,6 @@
1
1
  <template>
2
- <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover my-2">
3
- <v-col v-if="mdAndUp" cols="1">
2
+ <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover py-2">
3
+ <v-col v-if="mdAndUp" cols="2">
4
4
  <MiscAtomsDateStamp
5
5
  v-if="item.start"
6
6
  :loading
@@ -91,6 +91,7 @@
91
91
  import { useI18n, useNuxtApp, computed } from "#imports"
92
92
  import { useDisplay } from "vuetify"
93
93
  import { useRootStore } from "../../stores/root"
94
+ import { highlightAndTruncate } from "../../composables/useUtils"
94
95
  const { $rootStore } = useNuxtApp()
95
96
  const { smAndDown, mdAndUp } = useDisplay()
96
97
 
@@ -1,5 +1,9 @@
1
1
  <template>
2
- <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover">
2
+ <v-row
3
+ v-ripple
4
+ no-gutters
5
+ class="cursor-pointer highlight-on-hover pt-2 pl-2"
6
+ >
3
7
  <v-col align-self="center" cols="8" class="text-h5 dense">
4
8
  <v-skeleton-loader
5
9
  v-if="loading"
@@ -35,6 +39,7 @@
35
39
 
36
40
  <script setup>
37
41
  import { useNuxtApp } from "#imports"
42
+ import { highlightAndTruncate } from "../../composables/useUtils"
38
43
  const { $rootStore } = useNuxtApp()
39
44
 
40
45
  const props = defineProps({
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <component
3
3
  :is="itemTemplate"
4
- v-for="(item, index) in rootStore.results[type].items"
4
+ v-for="(item, index) in rootStore.results[type]?.items || []"
5
5
  :key="index"
6
6
  :item="item"
7
7
  :index="index"
@@ -27,7 +27,9 @@ const itemTemplate = computed(() =>
27
27
  capitalize(props.type) +
28
28
  capitalize($stores[props.type].view.name) +
29
29
  "Item"
30
- ).toString()
31
- )
30
+ ).toString(),
31
+ ),
32
32
  )
33
+ console.log("ResultsList props:", itemTemplate, $stores[props.type].view.name)
34
+ console
33
35
  </script>
@@ -1,34 +1,5 @@
1
1
  <template>
2
2
  <div class="d-flex align-center">
3
- <v-text-field
4
- :id="`global-search-input-${type}`"
5
- v-model.trim="search"
6
- :placeholder="
7
- type === 'all'
8
- ? t('search')
9
- : $t('list.search-type', [$t('items.' + type, 2)])
10
- "
11
- prepend-inner-icon="mdi-magnify"
12
- single-line
13
- class="transition-swing flex-grow-1"
14
- variant="outlined"
15
- hide-details
16
- clearable
17
- tile
18
- type="search"
19
- :loading="rootStore.loading"
20
- @keyup.enter="$router.push(localePath('/search'))"
21
- @click:append="$router.push(localePath('/search'))"
22
- >
23
- <!-- :loading="$nuxt.loading || $store.state.loading" :class="{ 'mt-3':
24
- $store.state.scrolled }" -->
25
- <template v-if="!search" #label>
26
- <div class="searchLabel">
27
- {{ $t("search") }}
28
- </div>
29
- </template>
30
- </v-text-field>
31
-
32
3
  <v-menu
33
4
  v-if="filter"
34
5
  v-model="filterMenuOpen"
@@ -40,7 +11,7 @@
40
11
  <v-btn
41
12
  v-bind="menuProps"
42
13
  :rounded="0"
43
- variant="text"
14
+ variant="outlined"
44
15
  size="large"
45
16
  height="56"
46
17
  >
@@ -73,13 +44,39 @@
73
44
  </v-list>
74
45
  </v-card>
75
46
  </v-menu>
47
+ <v-text-field
48
+ :id="`global-search-input-${type}`"
49
+ v-model.trim="search"
50
+ :placeholder="
51
+ type === 'all'
52
+ ? t('search')
53
+ : $t('list.search-type', [$t('items.' + type, 2)])
54
+ "
55
+ single-line
56
+ class="transition-swing flex-grow-1"
57
+ variant="outlined"
58
+ hide-details
59
+ clearable
60
+ tile
61
+ type="search"
62
+ :loading="rootStore.loading"
63
+ @keyup.enter="navigateToSearch"
64
+ @click:append="navigateToSearch"
65
+ >
66
+ <template v-if="!search" #label>
67
+ <div class="searchLabel">
68
+ {{ $t("search") }}
69
+ </div>
70
+ </template>
71
+ </v-text-field>
72
+
76
73
  <v-btn
77
74
  :rounded="0"
78
75
  variant="outlined"
79
76
  size="large"
80
77
  height="56"
81
- @keyup.enter="$router.push(localePath('/search'))"
82
- @click="$router.push(localePath('/search'))"
78
+ @keyup.enter="navigateToSearch"
79
+ @click="navigateToSearch"
83
80
  >
84
81
  <v-icon size="large">mdi-magnify</v-icon>
85
82
  <v-tooltip activator="parent" location="start">{{
@@ -92,7 +89,7 @@
92
89
  <script setup>
93
90
  import { useDebounceFn } from "@vueuse/core"
94
91
  import { useRootStore } from "../../../stores/root"
95
- import { computed, useI18n, ref, useLocalePath } from "#imports"
92
+ import { computed, useI18n, ref, useLocalePath, useRouter } from "#imports"
96
93
  const localePath = useLocalePath()
97
94
  const { locale, t } = useI18n()
98
95
  const rootStore = useRootStore()
@@ -150,20 +147,30 @@ const toggleFilter = (option) => {
150
147
  })
151
148
  }
152
149
 
150
+ const navigateToSearch = () => {
151
+ navigateTo({
152
+ path: localePath("/search"),
153
+ query: rootStore.search ? { search: rootStore.search } : {},
154
+ })
155
+ }
156
+
153
157
  const search = computed({
154
158
  get() {
155
159
  return rootStore.search
156
160
  },
157
161
  set: useDebounceFn(function (v) {
158
- emit("change", {
159
- name: "search",
160
- value: v,
161
- })
162
- rootStore.updateSearch({
163
- type: props.type,
164
- search: v || "",
165
- lang: locale.value,
166
- })
162
+ const value = v || ""
163
+ if (!value && !rootStore.search) return
164
+
165
+ if (props.type === "all") {
166
+ rootStore.search = value
167
+ } else {
168
+ rootStore.updateSearch({
169
+ type: props.type,
170
+ search: value,
171
+ lang: locale.value,
172
+ })
173
+ }
167
174
  }, 300),
168
175
  })
169
176
  </script>
@@ -6,7 +6,9 @@
6
6
  size="large"
7
7
  class=""
8
8
  @click="$emit('toggle', type)"
9
- :disabled="$rootStore.results[type]?.total === 0"
9
+ :disabled="
10
+ $rootStore.results[type] && $rootStore.results[type]?.total === 0
11
+ "
10
12
  >
11
13
  <v-icon size="large">{{
12
14
  open ? "mdi-chevron-down" : "mdi-chevron-right"
@@ -19,7 +21,9 @@
19
21
  <div
20
22
  class="text-h5 font-weight-medium"
21
23
  :class="
22
- $rootStore.results[type].total > 0 ? 'black' : 'text-grey darken-2'
24
+ $rootStore.results[type] && $rootStore.results[type].total > 0
25
+ ? 'black'
26
+ : 'text-grey darken-2'
23
27
  "
24
28
  >
25
29
  {{ capitalize($t("items." + props.type, 2)) }}
@@ -30,18 +34,26 @@
30
34
  ? $t(
31
35
  "list.0-items-found-f",
32
36
  [
33
- $rootStore.results[type].total,
34
- $t("items." + props.type, $rootStore.results[type].total),
37
+ $rootStore.results[type] && $rootStore.results[type].total,
38
+ $t(
39
+ "items." + props.type,
40
+ $rootStore.results[type] &&
41
+ $rootStore.results[type].total,
42
+ ),
35
43
  ],
36
- $rootStore.results[type].total,
44
+ $rootStore.results[type] && $rootStore.results[type].total,
37
45
  )
38
46
  : $t(
39
47
  "list.0-items-found",
40
48
  [
41
- $rootStore.results[type].total,
42
- $t("items." + props.type, $rootStore.results[type].total),
49
+ $rootStore.results[type] && $rootStore.results[type].total,
50
+ $t(
51
+ "items." + props.type,
52
+ $rootStore.results[type] &&
53
+ $rootStore.results[type].total,
54
+ ),
43
55
  ],
44
- $rootStore.results[type].total,
56
+ $rootStore.results[type] && $rootStore.results[type].total,
45
57
  )
46
58
  }}
47
59
  </div>
@@ -54,10 +66,14 @@
54
66
  color="default"
55
67
  variant="text"
56
68
  rounded="0"
57
- v-if="$rootStore.results[type]?.total > 0"
69
+ v-if="$rootStore.results[type] && $rootStore.results[type]?.total > 0"
58
70
  :to="localePath(type === 'people' ? '/people' : '/activities/' + type)"
59
71
  >
60
- {{ $t("list.pls-x-more", [$rootStore.results[type].total]) }}
72
+ {{
73
+ $t("list.pls-x-more", [
74
+ $rootStore.results[type] && $rootStore.results[type].total,
75
+ ])
76
+ }}
61
77
  </v-btn>
62
78
  </div>
63
79
  <v-divider></v-divider>
@@ -147,7 +147,7 @@ const { data, pending, error, refresh } = await useAsyncQuery(
147
147
  if (error.value) {
148
148
  console.error("GraphQL query error: ", error.value)
149
149
  } else {
150
- console.log("Query result data: ", data.value)
150
+ /* console.log("Query result data: ", data.value) */
151
151
  }
152
152
 
153
153
  // Apply data to store immediately if available
@@ -2,121 +2,123 @@
2
2
  <ListMoleculesGlobalSearchInput
3
3
  type="all"
4
4
  :placeholder="$t('search')"
5
- variant="outlined"
6
5
  :categories="selectedCategories"
7
- @change="updateSearch($event)"
6
+ filter
8
7
  @filter-change="handleFilterChange"
9
8
  />
10
- <ListMoleculesResultsContainer
11
- v-for="type in filteredSortedModules"
12
- :key="type"
13
- :feminine="type === 'people'"
14
- :type
15
- :open="$rootStore.results[type]?.total > 0 ?? open[type]"
16
- @toggle="open[$event] = !open[$event]"
17
- >
18
- {{ $rootStore.results[type]?.total }}
19
- <v-expand-transition class="results-container">
20
- <div v-show="$rootStore.results[type]?.total > 0 || open[type]">
21
- <ListAtomsResultsList :type />
22
- </div>
23
- </v-expand-transition>
24
- </ListMoleculesResultsContainer>
9
+ <v-progress-linear v-if="pending" indeterminate />
10
+ <template v-else>
11
+ <ListMoleculesResultsContainer
12
+ v-for="type in filteredSortedModules"
13
+ :key="type"
14
+ :feminine="type === 'people'"
15
+ :type
16
+ :open="
17
+ ($rootStore.results[type] && $rootStore.results[type]?.total > 0) ??
18
+ open[type]
19
+ "
20
+ @toggle="open[$event] = !open[$event]"
21
+ >
22
+ <v-expand-transition class="results-container">
23
+ <div
24
+ v-show="
25
+ ($rootStore.results[type] && $rootStore.results[type]?.total > 0) ||
26
+ open[type]
27
+ "
28
+ >
29
+ <ListAtomsResultsList :type />
30
+ </div>
31
+ </v-expand-transition>
32
+ </ListMoleculesResultsContainer>
33
+ </template>
25
34
  </template>
26
35
 
27
36
  <script setup>
28
37
  import {
29
38
  useNuxtApp,
30
- onBeforeUnmount,
31
39
  useI18n,
32
40
  useAppConfig,
41
+ useRoute,
33
42
  ref,
34
43
  useAsyncQuery,
35
44
  computed,
45
+ watch,
36
46
  } from "#imports"
37
47
  import SEARCH from "@paris-ias/trees/dist/graphql/client/misc/query.search.all.gql"
38
- // Component name for linting
39
- defineOptions({
40
- name: "SearchResults",
41
- })
42
48
 
43
49
  const { $rootStore } = useNuxtApp()
44
-
45
50
  const appConfig = useAppConfig()
46
51
  const { locale } = useI18n()
52
+ const route = useRoute()
47
53
  const open = ref({})
48
54
 
49
- // State for selected categories (default to all selected)
55
+ if (route.query.search) {
56
+ $rootStore.search = route.query.search
57
+ }
58
+
50
59
  const selectedCategories = ref([...appConfig.list.modules])
51
60
 
52
- // Handle filter changes
53
61
  const handleFilterChange = (filterData) => {
54
62
  selectedCategories.value = filterData.categories
55
63
  }
56
64
 
57
- // Computed property to sort modules by total count (highest first)
58
65
  const sortedModules = computed(() => {
59
66
  return appConfig.list.modules.slice().sort((a, b) => {
60
- const aResults = $rootStore.results[a] || { total: 0 }
61
- const bResults = $rootStore.results[b] || { total: 0 }
62
-
63
- // Sort by highest total count first
64
- return (bResults.total || 0) - (aResults.total || 0)
67
+ const aMaxScore = Math.max(
68
+ ...($rootStore.results[a]?.items || []).map((i) => i.score ?? 0),
69
+ 0,
70
+ )
71
+ const bMaxScore = Math.max(
72
+ ...($rootStore.results[b]?.items || []).map((i) => i.score ?? 0),
73
+ 0,
74
+ )
75
+ return bMaxScore - aMaxScore
65
76
  })
66
77
  })
67
-
68
- // Computed property to filter and sort modules based on selected categories
69
78
  const filteredSortedModules = computed(() => {
70
79
  return sortedModules.value.filter((type) =>
71
80
  selectedCategories.value.includes(type),
72
81
  )
73
82
  })
74
83
 
75
- // Apollo GraphQL query with proper reactivity
76
- const { data, pending, error, refresh } = await useAsyncQuery(
84
+ const searchTerm = computed(() => $rootStore.search || "")
85
+ const currentLocale = computed(() => locale.value)
86
+
87
+ const { data, pending, error } = useAsyncQuery(
77
88
  SEARCH,
78
- { search: $rootStore.search, appId: "iea", locale: locale.value },
89
+ computed(() => ({
90
+ search: searchTerm.value,
91
+ appId: "iea",
92
+ locale: currentLocale.value,
93
+ })),
79
94
  {
80
- key: `search-${$rootStore.search}`, // Unique key for caching
81
- server: true, // Enable SSR
95
+ enabled: computed(() => searchTerm.value.length > 0),
82
96
  },
83
97
  )
84
98
  if (error.value) {
85
99
  console.error("GraphQL query error: ", error.value)
86
100
  } else {
87
- console.log("Query result data: ", data.value.items?.length)
101
+ /* console.log("Query result data: ", data.value.items?.length) */
88
102
  }
89
103
 
90
- // Apply data to store immediately if available
91
- if (data.value) {
92
- console.log("Applying data to store directly [first load scenario]")
93
- $rootStore.applyListResult("all", data.value)
94
- // Initialize open state for types with results
95
- appConfig.list.modules.forEach((type) => {
96
- if ($rootStore.results[type]?.total > 0) {
97
- open.value[type] = true
98
- }
99
- })
100
- }
101
-
102
- const updateSearch = async (newSearch) => {
103
- console.log("update search")
104
- if (newSearch !== $rootStore.search) {
105
- await refresh()
106
- if (data.value) {
107
- console.log("Applying data to store directly [first load scenario]")
108
- $rootStore.applyListResult("all", data.value)
109
- // Initialize open state for types with results
104
+ watch(
105
+ data,
106
+ (newData) => {
107
+ if (newData) {
108
+ console.log("Applying search data to store")
109
+ $rootStore.applyListResult("all", newData)
110
110
  appConfig.list.modules.forEach((type) => {
111
- if ($rootStore.results[type]?.total > 0) {
111
+ if ($rootStore.results[type] && $rootStore.results[type]?.total > 0) {
112
112
  open.value[type] = true
113
113
  }
114
114
  })
115
115
  }
116
- }
117
- }
118
- onBeforeUnmount(() => {
119
- /* rootStore.resetState("all", locale.value) */
116
+ },
117
+ { immediate: true },
118
+ )
119
+
120
+ watch(error, (err) => {
121
+ if (err) console.error("GraphQL query error:", err)
120
122
  })
121
123
  </script>
122
124
  <style scoped>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <section class="my-6 ml-2 pb-6">
2
+ <section class="my-6 pb-6">
3
3
  <slot>
4
4
  <!-- fallback content -->
5
5
  </slot>
@@ -11,7 +11,7 @@
11
11
  <div class="overflow-hidden mw-100">
12
12
  <!-- TODO debug why the picture is not displaying/sizing properly -->
13
13
  <v-img
14
- v-if="src"
14
+ v-if="src && computedSrc"
15
15
  :aspect-ratio="ratio"
16
16
  :class="{ 'img-animation': animate }"
17
17
  :lazy-src="
@@ -43,7 +43,8 @@ import { computed, useImage } from "#imports"
43
43
  const img = useImage()
44
44
 
45
45
  const computedSrc = computed(() => {
46
- return typeof props.src === "string" ? props.src : props.src.url
46
+ if (!props.src) return null
47
+ return typeof props.src === "string" ? props.src : props.src?.url || null
47
48
  })
48
49
  const props = defineProps({
49
50
  src: {
@@ -68,17 +69,16 @@ const props = defineProps({
68
69
  animate: { type: Boolean, default: true },
69
70
  })
70
71
  const _srcset = computed(() => {
71
- return img.getSizes(
72
- typeof props.src === "string" ? props.src : props.src.url,
73
- {
74
- sizes: "xs:100vw sm:100vw md:100vw lg:100vw xl:100vw",
75
- modifiers: {
76
- format: "webp",
77
- quality: 70,
78
- ...(props.width && { width: props.width }),
79
- },
72
+ const srcUrl = typeof props.src === "string" ? props.src : props.src?.url
73
+ if (!srcUrl) return { srcset: "", sizes: "" }
74
+ return img.getSizes(srcUrl, {
75
+ sizes: "xs:100vw sm:100vw md:100vw lg:100vw xl:100vw",
76
+ modifiers: {
77
+ format: "webp",
78
+ quality: 70,
79
+ ...(props.width && { width: props.width }),
80
80
  },
81
- )
81
+ })
82
82
  })
83
83
  </script>
84
84
 
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover my-2">
2
+ <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover pa-2">
3
3
  <v-col v-if="mdAndUp" align-self="center" cols="1">
4
4
  <MiscAtomsImageContainer
5
5
  cover
@@ -11,7 +11,7 @@
11
11
  width="80px"
12
12
  />
13
13
  </v-col>
14
- <v-col align-self="start" class="text-h5 dense px-2 paragraph">
14
+ <v-col align-self="start" class="text-h5 dense mx-2 paragraph">
15
15
  <v-skeleton-loader v-if="loading" type="heading" />
16
16
  <template v-else>
17
17
  <v-skeleton-loader
@@ -25,7 +25,7 @@
25
25
 
26
26
  <template v-else>
27
27
  <v-chip
28
- class="ma-2"
28
+ class="mb-2"
29
29
  style="background-color: white; color: black"
30
30
  size="small"
31
31
  variant="outlined"
@@ -36,11 +36,11 @@
36
36
  </template>
37
37
  <div
38
38
  v-html="
39
- $rootStore.search.length
39
+ rootStore.search.length
40
40
  ? highlightAndTruncate(
41
41
  300,
42
42
  item.name,
43
- $rootStore.search.split(' '),
43
+ rootStore.search.split(' '),
44
44
  )
45
45
  : item.name
46
46
  "
@@ -52,9 +52,12 @@
52
52
 
53
53
  <script setup>
54
54
  import { useDisplay } from "vuetify"
55
+ import { useRootStore } from "../../stores/root"
56
+ import { highlightAndTruncate } from "../../composables/useUtils"
55
57
  import { computed } from "#imports"
56
58
 
57
59
  const { name, mdAndUp } = useDisplay()
60
+ const rootStore = useRootStore()
58
61
 
59
62
  const props = defineProps({
60
63
  item: {
@@ -68,7 +68,7 @@
68
68
  </v-col>
69
69
 
70
70
  <v-col v-if="lgAndUp" cols="12" lg="5">
71
- <v-skeleton-loader v-if="isLoading" type="text@8, ossein, button" />
71
+ <v-skeleton-loader v-if="loading" type="text@8, ossein, button" />
72
72
 
73
73
  <template v-else>
74
74
  <div
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover my-2">
2
+ <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover">
3
3
  <v-col v-if="mdAndUp" cols="1">
4
4
  <MiscAtomsImageContainer
5
5
  cover
@@ -9,6 +9,7 @@
9
9
  "
10
10
  :ratio="1 / 1"
11
11
  :width="80"
12
+ class="ma-1"
12
13
  />
13
14
  </v-col>
14
15
  <v-col align-self="start" class="text-h6 dense px-2">
@@ -20,7 +21,7 @@
20
21
  ? highlightAndTruncate(
21
22
  300,
22
23
  item.firstname + ' ' + item.lastname,
23
- $rootStore.search.split(' '),
24
+ rootStore.search.split(' '),
24
25
  )
25
26
  : item.firstname + ' ' + item.lastname
26
27
  "
@@ -36,7 +37,7 @@
36
37
  ? highlightAndTruncate(
37
38
  300,
38
39
  item.groups.vintage[0].theme,
39
- $rootStore.search.split(' '),
40
+ rootStore.search.split(' '),
40
41
  )
41
42
  : item.groups.vintage[0].theme
42
43
  "
@@ -47,6 +48,7 @@
47
48
 
48
49
  <script setup>
49
50
  import { useRootStore } from "../../stores/root"
51
+ import { highlightAndTruncate } from "../../composables/useUtils"
50
52
  import { computed } from "#imports"
51
53
  import { useDisplay } from "vuetify"
52
54
 
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover">
2
+ <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover px-4">
3
3
  <v-col v-if="mdAndUp" align-self="center" cols="1">
4
4
  <MiscAtomsImageContainer
5
5
  cover
@@ -11,13 +11,13 @@
11
11
  :width="50"
12
12
  />
13
13
  </v-col>
14
- <v-col align-self="center" class="text-h5 dense pl-2">
14
+ <v-col align-self="center" class="text-h5 dense pl-4 pt-2">
15
15
  <v-skeleton-loader v-if="loading" type="heading" width="50%" />
16
16
  <span
17
17
  v-else
18
18
  v-html="
19
19
  rootStore.search.length
20
- ? highlightAndTruncate(300, item.name, $rootStore.search.split(' '))
20
+ ? highlightAndTruncate(300, item.name, rootStore.search.split(' '))
21
21
  : item.name
22
22
  "
23
23
  />
@@ -32,7 +32,7 @@
32
32
 
33
33
  <MDC
34
34
  v-else-if="item.summary"
35
- class="text-caption font-weight-light paragraph"
35
+ class="text-caption font-weight-light paragraph mt-n2"
36
36
  :value="`${highlightAndTruncate(
37
37
  150,
38
38
  item.summary,
@@ -46,6 +46,7 @@
46
46
  <script setup>
47
47
  import { useDisplay } from "vuetify"
48
48
  import { useRootStore } from "../../stores/root"
49
+ import { highlightAndTruncate } from "../../composables/useUtils"
49
50
 
50
51
  const { mdAndUp } = useDisplay()
51
52
 
@@ -48,7 +48,7 @@
48
48
  ? highlightAndTruncate(
49
49
  300,
50
50
  item.name,
51
- $rootStore.search.split(' '),
51
+ rootStore.search.split(' '),
52
52
  )
53
53
  : item.name
54
54
  "
@@ -71,6 +71,7 @@
71
71
  <script setup>
72
72
  import { useDisplay } from "vuetify"
73
73
  import { useRootStore } from "../../stores/root"
74
+ import { highlightAndTruncate } from "../../composables/useUtils"
74
75
  import { computed } from "#imports"
75
76
  const rootStore = useRootStore()
76
77
 
@@ -54,8 +54,20 @@ export const useRootStore = defineStore("rootStore", {
54
54
  }
55
55
  });
56
56
  }
57
- if (query.page && query.page > 1)
58
- $stores[type].page = parseInt(query.page, 10) || 1;
57
+ if (query.search && typeof query.search === "string") {
58
+ if (type === "all") {
59
+ this.search = query.search;
60
+ } else if ($stores[type]) {
61
+ ;
62
+ $stores[type].search = query.search;
63
+ }
64
+ }
65
+ const page = parseInt(query.page, 10) || 1;
66
+ if (type === "all") {
67
+ this.page = page;
68
+ } else if ($stores[type]) {
69
+ $stores[type].page = page;
70
+ }
59
71
  },
60
72
  setFiltersCount(type) {
61
73
  const { $stores } = useNuxtApp();
@@ -70,9 +82,12 @@ export const useRootStore = defineStore("rootStore", {
70
82
  updateRouteQuery(type) {
71
83
  const router = useRouter();
72
84
  const { $stores } = useNuxtApp();
85
+ const isGlobal = type === "all";
86
+ const searchValue = isGlobal ? this.search : $stores[type]?.search || "";
87
+ const pageValue = isGlobal ? this.page : $stores[type]?.page || 1;
73
88
  const routeQuery = {
74
- ...this.search ? { search: this.search } : {},
75
- ...this.page > 1 ? { page: this.page.toString() } : {},
89
+ ...searchValue ? { search: searchValue } : {},
90
+ ...pageValue > 1 ? { page: pageValue.toString() } : {},
76
91
  ...Object.entries($stores[type]?.filters ?? {}).reduce(
77
92
  (acc, [key, filter]) => {
78
93
  const value = filter?.value;
@@ -125,7 +140,11 @@ export const useRootStore = defineStore("rootStore", {
125
140
  if ($stores[type]?.filters?.[key]) {
126
141
  $stores[type].filters[key].value = val;
127
142
  }
128
- this.page = 1;
143
+ if (type === "all") {
144
+ this.page = 1;
145
+ } else {
146
+ $stores[type].page = 1;
147
+ }
129
148
  $stores[type].loading = true;
130
149
  this.setFiltersCount(type);
131
150
  this.updateRouteQuery(type);
@@ -136,7 +155,11 @@ export const useRootStore = defineStore("rootStore", {
136
155
  lang
137
156
  }) {
138
157
  const { $stores } = useNuxtApp();
139
- this.page = 1;
158
+ if (type === "all") {
159
+ this.page = 1;
160
+ } else {
161
+ $stores[type].page = 1;
162
+ }
140
163
  $stores[type].itemsPerPage = value;
141
164
  $stores[type].loading = true;
142
165
  this.updateRouteQuery(type);
@@ -170,15 +193,16 @@ export const useRootStore = defineStore("rootStore", {
170
193
  }) {
171
194
  if (type === "all") {
172
195
  this.search = search;
196
+ this.page = 1;
173
197
  } else {
174
198
  const { $stores } = useNuxtApp();
175
199
  if ($stores[type]) {
176
200
  ;
177
201
  $stores[type].search = search;
202
+ $stores[type].page = 1;
178
203
  $stores[type].loading = true;
179
204
  }
180
205
  }
181
- this.page = 1;
182
206
  this.updateRouteQuery(type);
183
207
  },
184
208
  buildListVariables(type, lang = "en") {
@@ -194,21 +218,20 @@ export const useRootStore = defineStore("rootStore", {
194
218
  }
195
219
  }
196
220
  }
221
+ const localSearch = $stores[type]?.search || "";
197
222
  const args = JSON.parse(
198
223
  JSON.stringify({
199
224
  options: {
200
225
  skip: +$stores[type]?.page === 1 ? 0 : (+$stores[type]?.page - 1) * itemsPerPage,
201
226
  limit: itemsPerPage,
202
- ...this.search?.length && type !== "all" && { search: this.search },
227
+ ...type !== "all" && localSearch?.length && { search: localSearch },
203
228
  filters,
204
229
  sort: $stores[type]?.sortKey || ($stores[type]?.sort ? Object.keys($stores[type].sort).find(
205
230
  (key) => $stores[type].sort[key].default
206
231
  ) : void 0)
207
232
  },
208
233
  ...type === "all" && this.search?.length && { search: this.search },
209
- ...type !== "all" && $stores[type] && $stores[type].search?.length && {
210
- search: $stores[type].search
211
- },
234
+ ...type !== "all" && localSearch?.length && { search: localSearch },
212
235
  appId: "iea",
213
236
  lang
214
237
  })
@@ -220,7 +243,23 @@ export const useRootStore = defineStore("rootStore", {
220
243
  const { $stores } = useNuxtApp();
221
244
  const key = type === "all" ? "search" : "list" + type.charAt(0).toUpperCase() + type.slice(1);
222
245
  if (type === "all") {
223
- this.results = data?.[key] || this.results;
246
+ const searchData = data?.[key];
247
+ if (searchData) {
248
+ for (const moduleType of Object.keys(searchData)) {
249
+ if (moduleType in this.results) {
250
+ ;
251
+ this.results[moduleType] = searchData[moduleType];
252
+ }
253
+ }
254
+ }
255
+ for (const category of Object.keys(this.results)) {
256
+ const categoryData = this.results[category];
257
+ if (categoryData?.items?.length) {
258
+ categoryData.items.sort(
259
+ (a, b) => (b.score ?? 0) - (a.score ?? 0)
260
+ );
261
+ }
262
+ }
224
263
  return;
225
264
  }
226
265
  const items = data?.[key]?.items ?? [];
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.179",
4
+ "version": "1.0.181",
5
5
  "name": "@paris-ias/list",
6
6
  "repository": {
7
7
  "url": "git+https://github.com/IEA-Paris/list.git",