@paris-ias/list 1.0.180 → 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 +1 -1
- package/dist/runtime/components/events/DenseItem.vue +2 -2
- package/dist/runtime/components/fellowships/DenseItem.vue +5 -1
- package/dist/runtime/components/list/atoms/ResultsList.vue +5 -3
- package/dist/runtime/components/list/molecules/GlobalSearchInput.vue +49 -42
- package/dist/runtime/components/list/molecules/ResultsContainer.vue +26 -10
- package/dist/runtime/components/list/organisms/Results.vue +65 -119
- package/dist/runtime/components/list/views/Dense.vue +1 -1
- package/dist/runtime/components/news/DenseItem.vue +3 -3
- package/dist/runtime/components/news/RowsItem.vue +1 -1
- package/dist/runtime/components/people/DenseItem.vue +2 -1
- package/dist/runtime/components/projects/DenseItem.vue +3 -3
- package/dist/runtime/stores/root.js +51 -12
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover
|
|
3
|
-
<v-col v-if="mdAndUp" cols="
|
|
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
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-row
|
|
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"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<component
|
|
3
3
|
:is="itemTemplate"
|
|
4
|
-
v-for="(item, index) in rootStore.results[type]
|
|
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="
|
|
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="
|
|
82
|
-
@click="
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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="
|
|
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
|
|
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(
|
|
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(
|
|
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
|
-
{{
|
|
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>
|
|
@@ -2,83 +2,97 @@
|
|
|
2
2
|
<ListMoleculesGlobalSearchInput
|
|
3
3
|
type="all"
|
|
4
4
|
:placeholder="$t('search')"
|
|
5
|
-
variant="outlined"
|
|
6
5
|
:categories="selectedCategories"
|
|
7
|
-
|
|
6
|
+
filter
|
|
8
7
|
@filter-change="handleFilterChange"
|
|
9
8
|
/>
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
76
|
-
const
|
|
84
|
+
const searchTerm = computed(() => $rootStore.search || "")
|
|
85
|
+
const currentLocale = computed(() => locale.value)
|
|
86
|
+
|
|
87
|
+
const { data, pending, error } = useAsyncQuery(
|
|
77
88
|
SEARCH,
|
|
78
|
-
|
|
89
|
+
computed(() => ({
|
|
90
|
+
search: searchTerm.value,
|
|
91
|
+
appId: "iea",
|
|
92
|
+
locale: currentLocale.value,
|
|
93
|
+
})),
|
|
79
94
|
{
|
|
80
|
-
|
|
81
|
-
server: true, // Enable SSR
|
|
95
|
+
enabled: computed(() => searchTerm.value.length > 0),
|
|
82
96
|
},
|
|
83
97
|
)
|
|
84
98
|
if (error.value) {
|
|
@@ -87,93 +101,25 @@ if (error.value) {
|
|
|
87
101
|
/* console.log("Query result data: ", data.value.items?.length) */
|
|
88
102
|
}
|
|
89
103
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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) */
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
const socioscope = {
|
|
123
|
-
appId: ["iea"],
|
|
124
|
-
url: "https://thesocioscope.org",
|
|
125
|
-
subtitle: {
|
|
126
|
-
en: "Seeing How Social Change Takes Shape",
|
|
127
|
-
fr: "Voir comment le changement social prend forme",
|
|
128
|
-
},
|
|
129
|
-
date: {
|
|
130
|
-
$date: "2023-03-18T00:00:00.000Z",
|
|
131
|
-
},
|
|
132
|
-
image: {
|
|
133
|
-
url: "https://cdn-yggdrasil-dev.s3.eu-west-2.amazonaws.com/iea/project/the_socioscope.svg",
|
|
134
|
-
},
|
|
135
|
-
summary: {
|
|
136
|
-
en: "Through large scale data collection around the world and LLM-driven research, the Socioscope analyses how behaviours, norms, and practices evolve within sustainable food transitions.\nWe're building a systemic way to help make complex social patterns measurable, comparable, and understandable.",
|
|
137
|
-
fr: "Grâce à la collecte de données à grande échelle dans le monde entier et à la recherche pilotée par LLM, le Socioscope analyse comment les comportements, normes et pratiques évoluent dans les transitions alimentaires durables.\nNous construisons une méthode systématique pour rendre les schémas sociaux complexes mesurables, comparables et compréhensibles.",
|
|
138
|
-
},
|
|
139
|
-
description: {
|
|
140
|
-
en: "The Socioscope is a transformative qualitative research project that maps how behaviours, norms, and communities influence the transition toward sustainable food systems.\nThe Socioscope serves as a comprehensive observatory of these efforts, making knowledge accessible to researchers, policymakers, and practitioners worldwide. Equally important is shining a light on local initiatives that lead change on the ground and drive the shift toward a more sustainable food system.",
|
|
141
|
-
fr: "Le Socioscope est un projet de recherche qualitative transformatif qui cartographie comment les comportements, normes et communautés influencent la transition vers des systèmes alimentaires durables.\nLe Socioscope sert d'observatoire complet de ces efforts, rendant les connaissances accessibles aux chercheurs, décideurs politiques et praticiens du monde entier. Tout aussi important, il met en lumière les initiatives locales qui mènent le changement sur le terrain et conduisent la transition vers un système alimentaire plus durable.",
|
|
142
|
-
},
|
|
143
|
-
name: {
|
|
144
|
-
en: "The Socioscope",
|
|
145
|
-
fr: "Le Socioscope",
|
|
146
116
|
},
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
url: "https://cdn-yggdrasil-dev.s3.eu-west-2.amazonaws.com/iea/people/antoine_cordelois.jpg",
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
firstname: "Saadi",
|
|
158
|
-
lastname: "Lahlou",
|
|
159
|
-
image: {
|
|
160
|
-
url: "https://cdn-yggdrasil-dev.s3.eu-west-2.amazonaws.com/iea/people/lahlou_saadi.jpg",
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
firstname: "Paulius",
|
|
165
|
-
lastname: "Yamin",
|
|
166
|
-
image: {
|
|
167
|
-
url: "https://cdn-yggdrasil-dev.s3.eu-west-2.amazonaws.com/iea/people/Paulius_Yamin.jpg",
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
],
|
|
171
|
-
},
|
|
172
|
-
slug: {
|
|
173
|
-
fr: "le-socioscope",
|
|
174
|
-
en: "the-socioscope",
|
|
175
|
-
},
|
|
176
|
-
}
|
|
117
|
+
{ immediate: true },
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
watch(error, (err) => {
|
|
121
|
+
if (err) console.error("GraphQL query error:", err)
|
|
122
|
+
})
|
|
177
123
|
</script>
|
|
178
124
|
<style scoped>
|
|
179
125
|
.results-container{display:flex;flex-direction:column;gap:8px;margin-left:8px}
|
|
@@ -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 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
|
|
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="
|
|
28
|
+
class="mb-2"
|
|
29
29
|
style="background-color: white; color: black"
|
|
30
30
|
size="small"
|
|
31
31
|
variant="outlined"
|
|
@@ -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">
|
|
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">
|
|
@@ -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,7 +11,7 @@
|
|
|
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
|
|
@@ -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,
|
|
@@ -54,8 +54,20 @@ export const useRootStore = defineStore("rootStore", {
|
|
|
54
54
|
}
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
|
-
if (query.
|
|
58
|
-
|
|
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
|
-
...
|
|
75
|
-
...
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
...
|
|
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" &&
|
|
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
|
-
|
|
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 ?? [];
|