@saooti/octopus-sdk 41.1.14 → 41.2.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/CHANGELOG.md +42 -0
- package/index.ts +24 -1
- package/package.json +1 -1
- package/src/api/groupsApi.ts +214 -0
- package/src/api/podcastApi.ts +47 -9
- package/src/components/buttons/ActionButton.vue +99 -0
- package/src/components/buttons/index.ts +5 -0
- package/src/components/composable/route/types.ts +11 -3
- package/src/components/composable/route/useAdvancedParamInit.ts +38 -13
- package/src/components/composable/useErrorHandler.ts +3 -2
- package/src/components/composable/useNotifications.ts +50 -0
- package/src/components/display/emission/EmissionGroupChooser.vue +56 -0
- package/src/components/display/emission/EmissionItem.vue +23 -3
- package/src/components/display/emission/EmissionList.vue +8 -2
- package/src/components/display/filter/AdvancedSearch.vue +82 -23
- package/src/components/display/list/ListPaginate.vue +4 -1
- package/src/components/display/podcasts/PodcastList.vue +12 -5
- package/src/components/display/podcasts/PodcastPlayButton.vue +2 -2
- package/src/components/display/podcasts/TagList.vue +4 -1
- package/src/components/form/ClassicMultiselect.vue +43 -37
- package/src/components/icons.ts +13 -0
- package/src/components/misc/ClassicAlert.vue +8 -1
- package/src/components/misc/ClassicBigChip.vue +84 -0
- package/src/components/misc/ClassicDataTable.vue +98 -0
- package/src/components/misc/ClassicDataTable_Internal.vue +132 -0
- package/src/components/misc/ClassicHelpButton.vue +3 -3
- package/src/components/misc/ClassicNotifications.vue +23 -0
- package/src/components/misc/ClassicPopover.vue +1 -0
- package/src/components/pages/EmissionPage.vue +2 -2
- package/src/components/pages/EmissionsPage.vue +10 -15
- package/src/components/pages/PodcastPage.vue +1 -1
- package/src/components/pages/PodcastsPage.vue +8 -20
- package/src/helper/fetchHelper.ts +1 -0
- package/src/locale/de.ts +2 -0
- package/src/locale/en.ts +2 -0
- package/src/locale/es.ts +2 -0
- package/src/locale/fr.ts +5 -3
- package/src/locale/it.ts +2 -0
- package/src/locale/sl.ts +2 -0
- package/src/router/router.ts +38 -53
- package/src/stores/class/general/emission.ts +8 -2
- package/src/style/_variables.scss +3 -0
- package/src/style/bootstrap.scss +5 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
|
|
3
|
+
type NotificationType = 'success'|'info'|'warning'|'error';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Type for notifications
|
|
7
|
+
*/
|
|
8
|
+
interface Notification {
|
|
9
|
+
/** Optional title of the notification */
|
|
10
|
+
title?: string;
|
|
11
|
+
/** Message displayed by the notification */
|
|
12
|
+
message: string;
|
|
13
|
+
/** Type of notification, may affect display */
|
|
14
|
+
type: NotificationType;
|
|
15
|
+
/** When set to false, disallow manual closing of modal notification (default: true) */
|
|
16
|
+
closeable?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The notification currently displayed */
|
|
20
|
+
const notification = ref<Notification|null>(null);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Composable used to manage notifications
|
|
24
|
+
*/
|
|
25
|
+
export const useNotifications = () => {
|
|
26
|
+
/**
|
|
27
|
+
* Add & display a notification
|
|
28
|
+
* @param newNotif The data of the new notification
|
|
29
|
+
*/
|
|
30
|
+
function addNotification(newNotif: Notification): void {
|
|
31
|
+
notification.value = { ...newNotif };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Remove all notifications
|
|
36
|
+
*/
|
|
37
|
+
function clearNotifications(): void {
|
|
38
|
+
notification.value = null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
/** The currently active notification */
|
|
43
|
+
notification,
|
|
44
|
+
|
|
45
|
+
/** Add a new notification to be displayed */
|
|
46
|
+
addNotification,
|
|
47
|
+
/** Remove all active notifications */
|
|
48
|
+
clearNotifications
|
|
49
|
+
}
|
|
50
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ClassicMultiselect
|
|
3
|
+
id="group-chooser"
|
|
4
|
+
ref="selectGroup"
|
|
5
|
+
option-label="name"
|
|
6
|
+
:placeholder="$t('Search - Emission groups placeholder')"
|
|
7
|
+
:max-element="maxElement"
|
|
8
|
+
width="400px"
|
|
9
|
+
in-modal
|
|
10
|
+
:option-chosen="groups"
|
|
11
|
+
multiple
|
|
12
|
+
@on-search="onSearch"
|
|
13
|
+
@selected="emitSelected"
|
|
14
|
+
/>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script setup lang="ts">
|
|
18
|
+
import { useTemplateRef } from "vue";
|
|
19
|
+
|
|
20
|
+
import ClassicMultiselect from "../../form/ClassicMultiselect.vue";
|
|
21
|
+
import { groupsApi, EmissionGroup } from "../../../api/groupsApi";
|
|
22
|
+
|
|
23
|
+
//Props
|
|
24
|
+
const props = defineProps<{
|
|
25
|
+
/** Filter by organisation */
|
|
26
|
+
organisationId?: Array<string>;
|
|
27
|
+
/** Currently selected groups */
|
|
28
|
+
groups: Array<EmissionGroup>;
|
|
29
|
+
}>();
|
|
30
|
+
|
|
31
|
+
//Emits
|
|
32
|
+
const emit = defineEmits(["update:groups"]);
|
|
33
|
+
|
|
34
|
+
//Data
|
|
35
|
+
const maxElement = 50;
|
|
36
|
+
const selectGroupRef = useTemplateRef('selectGroup');
|
|
37
|
+
|
|
38
|
+
//Methods
|
|
39
|
+
async function onSearch(query?: string): Promise<void> {
|
|
40
|
+
const response = await groupsApi.search({
|
|
41
|
+
first: 0,
|
|
42
|
+
size: maxElement,
|
|
43
|
+
search: query,
|
|
44
|
+
organisationIds: props.organisationId,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
selectGroupRef.value!.afterSearch(
|
|
48
|
+
response.result.filter(g => g.emissionIds?.length ?? 0 > 0),
|
|
49
|
+
response.count
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function emitSelected(option: Array<EmissionGroup>) {
|
|
54
|
+
emit("update:groups", option);
|
|
55
|
+
}
|
|
56
|
+
</script>
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
<!-- eslint-disable vue/no-v-html -->
|
|
39
39
|
<div
|
|
40
40
|
ref="descriptionEmission"
|
|
41
|
-
v-html="
|
|
41
|
+
v-html="description"
|
|
42
42
|
/>
|
|
43
43
|
<!-- eslint-enable -->
|
|
44
44
|
</div>
|
|
@@ -71,6 +71,7 @@ import { ListClassicReturn } from "@/stores/class/general/listReturn";
|
|
|
71
71
|
import ClassicImageBanner from '../../misc/ClassicImageBanner.vue';
|
|
72
72
|
|
|
73
73
|
import { useI18n } from "vue-i18n";
|
|
74
|
+
import { useResizePhone } from "../../composable/useResizePhone";
|
|
74
75
|
|
|
75
76
|
//Props
|
|
76
77
|
const props = defineProps<{
|
|
@@ -111,6 +112,25 @@ onMounted(()=>{
|
|
|
111
112
|
}
|
|
112
113
|
})
|
|
113
114
|
|
|
115
|
+
const { isPhone } = useResizePhone();
|
|
116
|
+
const description = computed((): string => {
|
|
117
|
+
let str = props.emission.description;
|
|
118
|
+
if (!str) {
|
|
119
|
+
return '';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Truncate description on phones
|
|
123
|
+
if (isPhone.value === true) {
|
|
124
|
+
const pattern = /^(.+?<\/p>)/;
|
|
125
|
+
const matches = str.match(pattern);
|
|
126
|
+
if (matches && matches.length === 2) {
|
|
127
|
+
str = matches[1];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return urlify(str);
|
|
132
|
+
});
|
|
133
|
+
|
|
114
134
|
//Methods
|
|
115
135
|
function urlify(text:string|undefined){
|
|
116
136
|
return displayHelper.urlify(text);
|
|
@@ -142,8 +162,8 @@ article {
|
|
|
142
162
|
max-height: 500px;
|
|
143
163
|
|
|
144
164
|
a {
|
|
145
|
-
flex-direction: column;
|
|
146
|
-
flex-wrap: nowrap;
|
|
165
|
+
flex-direction: column !important;
|
|
166
|
+
flex-wrap: nowrap !important;
|
|
147
167
|
}
|
|
148
168
|
|
|
149
169
|
.element-name {
|
|
@@ -71,6 +71,7 @@ import { Rubriquage } from "@/stores/class/rubrique/rubriquage";
|
|
|
71
71
|
import { useFilterStore } from "../../../stores/FilterStore";
|
|
72
72
|
import { ListClassicReturn } from "@/stores/class/general/listReturn";
|
|
73
73
|
import { useI18n } from "vue-i18n";
|
|
74
|
+
import { EmissionGroup } from "../../../api/groupsApi";
|
|
74
75
|
const EmissionItem = defineAsyncComponent(() => import("./EmissionItem.vue"));
|
|
75
76
|
const EmissionPlayerItem = defineAsyncComponent(
|
|
76
77
|
() => import("./EmissionPlayerItem.vue"),
|
|
@@ -94,7 +95,9 @@ const props = defineProps({
|
|
|
94
95
|
noRubriquageId: { default: () => [], type: Array as () => Array<number> },
|
|
95
96
|
nbPodcasts: { default: undefined, type: Number },
|
|
96
97
|
/** The beneficiaries to filter on */
|
|
97
|
-
beneficiaries: { default: null, type: Array as () => Array<string> }
|
|
98
|
+
beneficiaries: { default: null, type: Array as () => Array<string> },
|
|
99
|
+
/** The emission groups to filter on */
|
|
100
|
+
emissionGroups: { default: null, type: Array as () => Array<EmissionGroup> }
|
|
98
101
|
})
|
|
99
102
|
|
|
100
103
|
//Data
|
|
@@ -128,7 +131,8 @@ const changePaginate = computed(() => `${props.first}|${props.size}`);
|
|
|
128
131
|
const changed = computed(() => {
|
|
129
132
|
return `${props.organisationId}|${props.query}|${props.monetisable}|${props.includeHidden}|\
|
|
130
133
|
${props.iabId}|${props.rubriqueId}|${props.rubriquageId}|${props.before}|\
|
|
131
|
-
${props.after}|${props.sort}|${props.noRubriquageId}|${props.beneficiaries}
|
|
134
|
+
${props.after}|${props.sort}|${props.noRubriquageId}|${props.beneficiaries}|\
|
|
135
|
+
${props.emissionGroups}`;
|
|
132
136
|
});
|
|
133
137
|
const sortText = computed(() => {
|
|
134
138
|
let textSort = "";
|
|
@@ -204,6 +208,8 @@ async function fetchContent(reset: boolean): Promise<void> {
|
|
|
204
208
|
param.visible = 'VISIBLE';
|
|
205
209
|
}
|
|
206
210
|
|
|
211
|
+
// TODO use emissionGroups
|
|
212
|
+
|
|
207
213
|
try {
|
|
208
214
|
const data = await classicApi.fetchData<ListClassicReturn<Emission>>({
|
|
209
215
|
api: 0,
|
|
@@ -32,6 +32,23 @@
|
|
|
32
32
|
@update:rubrique-filter="updateRubriquageFilter"
|
|
33
33
|
/>
|
|
34
34
|
|
|
35
|
+
<!-- Group filters -->
|
|
36
|
+
<div v-if="!isEmission && showEmissionGroups" class="mt-3 d-flex">
|
|
37
|
+
<ClassicCheckbox
|
|
38
|
+
v-model:text-init="emissionGroupCheckbox"
|
|
39
|
+
class="flex-shrink-0"
|
|
40
|
+
id-checkbox="search-emission-groups-checkbox"
|
|
41
|
+
:label="t('Filters - Emission groups')"
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
<EmissionGroupChooser
|
|
45
|
+
v-if="emissionGroupCheckbox"
|
|
46
|
+
class="ms-4 flex-grow-1"
|
|
47
|
+
:groups="emissionGroups"
|
|
48
|
+
@update:groups="updateEmissionGroupFilter"
|
|
49
|
+
/>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
35
52
|
<!-- Date -->
|
|
36
53
|
<DateFilter
|
|
37
54
|
:is-emission="isEmission"
|
|
@@ -117,7 +134,7 @@ import { useAuthStore } from "../../../stores/AuthStore";
|
|
|
117
134
|
import { useFilterStore } from "../../../stores/FilterStore";
|
|
118
135
|
import { useRubriquesFilterParam } from "../../composable/route/useRubriquesFilterParam";
|
|
119
136
|
import { RubriquageFilter } from "@/stores/class/rubrique/rubriquageFilter";
|
|
120
|
-
import { defineAsyncComponent, ref, computed, watch } from "vue";
|
|
137
|
+
import { defineAsyncComponent, ref, computed, watch, onMounted } from "vue";
|
|
121
138
|
import { useGeneralStore } from "../../../stores/GeneralStore";
|
|
122
139
|
import { useI18n } from "vue-i18n";
|
|
123
140
|
const MonetizableFilter = defineAsyncComponent(
|
|
@@ -141,27 +158,34 @@ const ClassicCheckbox = defineAsyncComponent(
|
|
|
141
158
|
const DateFilter = defineAsyncComponent(() => import("./DateFilter.vue"));
|
|
142
159
|
const SearchOrder = defineAsyncComponent(() => import("./SearchOrder.vue"));
|
|
143
160
|
import { ROUTE_PARAMS } from '../../composable/route/types';
|
|
161
|
+
import { EmissionGroup, groupsApi } from "../../../api/groupsApi";
|
|
162
|
+
import EmissionGroupChooser from "../emission/EmissionGroupChooser.vue";
|
|
144
163
|
|
|
145
164
|
//Props
|
|
146
|
-
const props = defineProps
|
|
147
|
-
organisationId
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
165
|
+
const props = withDefaults(defineProps<{
|
|
166
|
+
organisationId?: string;
|
|
167
|
+
/** Indicates that the filters apply to emissions */
|
|
168
|
+
isEmission?: boolean;
|
|
169
|
+
includeHidden?: boolean;
|
|
170
|
+
sort?: string;
|
|
171
|
+
onlyVideo?: boolean;
|
|
172
|
+
monetisable?: string;
|
|
173
|
+
iabId?: number;
|
|
174
|
+
searchPattern?: string;
|
|
175
|
+
fromDate?: string;
|
|
176
|
+
toDate?: string;
|
|
177
|
+
validity?: string;
|
|
158
178
|
/** The filter on beneficiaries */
|
|
159
|
-
beneficiaries
|
|
160
|
-
rubriqueFilter
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
179
|
+
beneficiaries?: Array<string>;
|
|
180
|
+
rubriqueFilter?: Array<RubriquageFilter>;
|
|
181
|
+
/** The filter on groups */
|
|
182
|
+
emissionGroups?: Array<EmissionGroup>;
|
|
183
|
+
}>(), {
|
|
184
|
+
sort: "DATE",
|
|
185
|
+
monetisable: "UNDEFINED",
|
|
186
|
+
searchPattern: "",
|
|
187
|
+
validity: "true"
|
|
188
|
+
});
|
|
165
189
|
|
|
166
190
|
//Emits
|
|
167
191
|
const emit = defineEmits([
|
|
@@ -174,13 +198,14 @@ const emit = defineEmits([
|
|
|
174
198
|
"update:validity",
|
|
175
199
|
"update:rubriqueFilter",
|
|
176
200
|
"update:onlyVideo",
|
|
177
|
-
"update:beneficiaries"
|
|
201
|
+
"update:beneficiaries",
|
|
202
|
+
"update:emission-groups"
|
|
178
203
|
]);
|
|
179
204
|
|
|
180
205
|
//Data
|
|
181
206
|
const showFilters = ref(false);
|
|
182
207
|
const firstLoaded = ref(false);
|
|
183
|
-
|
|
208
|
+
const showEmissionGroups = ref(false);
|
|
184
209
|
|
|
185
210
|
//Composables
|
|
186
211
|
const { t } = useI18n();
|
|
@@ -190,6 +215,13 @@ const generalStore = useGeneralStore();
|
|
|
190
215
|
const filterStore = useFilterStore();
|
|
191
216
|
const authStore = useAuthStore();
|
|
192
217
|
|
|
218
|
+
onMounted(async() => {
|
|
219
|
+
// Only show emission groups if there are some
|
|
220
|
+
const nbGroups = await groupsApi.count({
|
|
221
|
+
organisationIds: [props.organisationId]
|
|
222
|
+
});
|
|
223
|
+
showEmissionGroups.value = nbGroups > 0;
|
|
224
|
+
});
|
|
193
225
|
|
|
194
226
|
//Computed
|
|
195
227
|
const organisationRight = computed(() => isEditRights(props.organisationId));
|
|
@@ -205,6 +237,7 @@ const isSelectValidity = computed(() => {
|
|
|
205
237
|
props.includeHidden
|
|
206
238
|
);
|
|
207
239
|
});
|
|
240
|
+
|
|
208
241
|
/** The beneficiaries filter is only displayed if beneficiaries are enabled */
|
|
209
242
|
const beneficiariesEnabled = computed(() => {
|
|
210
243
|
return authStore.authOrganisation.attributes['beneficiaries.enabled'] === 'true';
|
|
@@ -288,11 +321,37 @@ function updateRubriquageFilter(value: Array<RubriquageFilter>) {
|
|
|
288
321
|
filterRubriques = { rubriquesId: undefined };
|
|
289
322
|
}
|
|
290
323
|
updateRouteParamAdvanced({
|
|
291
|
-
|
|
324
|
+
r: valueString.length ? valueString : undefined,
|
|
292
325
|
...filterRubriques,
|
|
293
326
|
});
|
|
294
327
|
}
|
|
295
328
|
|
|
329
|
+
/** Manage the checkbox enabling filtering on groups */
|
|
330
|
+
const emissionGroupCheckbox = computed({
|
|
331
|
+
get(): boolean {
|
|
332
|
+
return props.emissionGroups !== undefined && props.emissionGroups !== null;
|
|
333
|
+
},
|
|
334
|
+
set(value: boolean): void {
|
|
335
|
+
if (value) {
|
|
336
|
+
updateEmissionGroupFilter([]);
|
|
337
|
+
} else {
|
|
338
|
+
updateEmissionGroupFilter(undefined);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
/** Update selected groups */
|
|
344
|
+
function updateEmissionGroupFilter(groups: Array<EmissionGroup>|undefined): void {
|
|
345
|
+
if (groups !== undefined && groups.length === 0) {
|
|
346
|
+
emit('update:emission-groups', []);
|
|
347
|
+
} else {
|
|
348
|
+
updateRouteParamAdvanced({
|
|
349
|
+
[ROUTE_PARAMS.EmissionGroups]: groups?.map(g => g.groupId)
|
|
350
|
+
});
|
|
351
|
+
emit('update:emission-groups', groups);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
296
355
|
/** Update the beneficiaries filter */
|
|
297
356
|
function updateBeneficiaries(value: string[]|undefined): void {
|
|
298
357
|
emit('update:beneficiaries', value);
|
|
@@ -350,4 +409,4 @@ function clickShowFilters(): void {
|
|
|
350
409
|
overflow: hidden;
|
|
351
410
|
}
|
|
352
411
|
}
|
|
353
|
-
</style>
|
|
412
|
+
</style>
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :id="id" class="d-flex flex-column align-items-center">
|
|
3
|
-
<ClassicLoading
|
|
3
|
+
<ClassicLoading
|
|
4
|
+
v-if="loading || errorText"
|
|
5
|
+
:loading-text="loadingText" :error-text="errorText"
|
|
6
|
+
/>
|
|
4
7
|
<template v-if="!loading">
|
|
5
8
|
<div
|
|
6
9
|
v-if="!justSizeChosen"
|
|
@@ -56,17 +56,15 @@
|
|
|
56
56
|
<script setup lang="ts">
|
|
57
57
|
import ListPaginate from "../list/ListPaginate.vue";
|
|
58
58
|
import {useErrorHandler} from "../../composable/useErrorHandler";
|
|
59
|
-
import classicApi from "../../../api/classicApi";
|
|
60
59
|
import PodcastItem from "./PodcastItem.vue";
|
|
61
60
|
import ClassicLazy from "../../misc/ClassicLazy.vue";
|
|
62
61
|
import { useFilterStore } from "../../../stores/FilterStore";
|
|
63
62
|
import { Podcast, PodcastProcessingStatus, emptyPodcastData } from "../../../stores/class/general/podcast";
|
|
64
63
|
import { computed, onBeforeMount, Ref, ref, watch } from "vue";
|
|
65
|
-
import { FetchParam } from "@/stores/class/general/fetchParam";
|
|
66
64
|
import { AxiosError } from "axios";
|
|
67
|
-
import { ListClassicReturn } from "../../../stores/class/general/listReturn";
|
|
68
65
|
import { useI18n } from "vue-i18n";
|
|
69
66
|
import { podcastApi, PodcastMonetisation, PodcastSearchOptions, PodcastSort } from "../../../api/podcastApi";
|
|
67
|
+
import { EmissionGroup } from "@/api/groupsApi";
|
|
70
68
|
|
|
71
69
|
//Props
|
|
72
70
|
const props = withDefaults(defineProps<{
|
|
@@ -96,6 +94,8 @@ const props = withDefaults(defineProps<{
|
|
|
96
94
|
forceUpdateParameters?: boolean;
|
|
97
95
|
/** The beneficiaries to filter on */
|
|
98
96
|
beneficiaries?: Array<string>;
|
|
97
|
+
/** The emission groups to filter on */
|
|
98
|
+
emissionGroups?: Array<EmissionGroup>;
|
|
99
99
|
}>(), {
|
|
100
100
|
first: 0,
|
|
101
101
|
size: 30,
|
|
@@ -106,6 +106,7 @@ const props = withDefaults(defineProps<{
|
|
|
106
106
|
displaySortText: true,
|
|
107
107
|
validity: true,
|
|
108
108
|
justSizeChosen: false,
|
|
109
|
+
withVideo: undefined,
|
|
109
110
|
forceUpdateParameters: false
|
|
110
111
|
});
|
|
111
112
|
|
|
@@ -140,7 +141,7 @@ const changed = computed(() => {
|
|
|
140
141
|
return `${organisation.value}|${props.emissionId}|${props.sortCriteria}|${sort.value}
|
|
141
142
|
${props.iabId}|${props.participantId}|${props.query}|${props.monetisable}|${props.popularSort}|
|
|
142
143
|
${props.rubriqueId}|${props.rubriquageId}|${props.before}|${props.after}|${props.includeHidden}|${props.noRubriquageId}|${props.validity}|
|
|
143
|
-
${props.withVideo}|${props.includeTag}|${props.beneficiaries}`;
|
|
144
|
+
${props.withVideo}|${props.includeTag}|${props.beneficiaries}|${props.emissionGroups}`;
|
|
144
145
|
});
|
|
145
146
|
const organisation = computed(() => {
|
|
146
147
|
if (props.organisationId) {
|
|
@@ -200,6 +201,7 @@ async function fetchContent(reset: boolean): Promise<void> {
|
|
|
200
201
|
pageSize: dsize.value,
|
|
201
202
|
organisationId: organisation.value,
|
|
202
203
|
emissionId: props.emissionId,
|
|
204
|
+
emissionGroups: props.emissionGroups,
|
|
203
205
|
iabId: props.iabId,
|
|
204
206
|
participantId: props.participantId,
|
|
205
207
|
query: props.query,
|
|
@@ -223,11 +225,16 @@ async function fetchContent(reset: boolean): Promise<void> {
|
|
|
223
225
|
tags: props.includeTag?.length ? props.includeTag : undefined,
|
|
224
226
|
beneficiaries: props.beneficiaries ?? undefined
|
|
225
227
|
};
|
|
228
|
+
|
|
226
229
|
try {
|
|
227
230
|
const data = await podcastApi.searchFull(param, true);
|
|
228
231
|
afterFetching(reset, data);
|
|
229
232
|
} catch (error) {
|
|
230
|
-
|
|
233
|
+
if (error instanceof AxiosError) {
|
|
234
|
+
handle403(error as AxiosError);
|
|
235
|
+
} else {
|
|
236
|
+
console.error('error', error);
|
|
237
|
+
}
|
|
231
238
|
}
|
|
232
239
|
}
|
|
233
240
|
function afterFetching(
|
|
@@ -47,11 +47,11 @@
|
|
|
47
47
|
@mouseenter="hoverType = 'video'"
|
|
48
48
|
@mouseleave="hoverType = ''"
|
|
49
49
|
>
|
|
50
|
+
<PodcastIsPlaying v-if="playingPodcast && playerStore.playerVideo" />
|
|
50
51
|
<PlayVideoIcon
|
|
51
|
-
v-
|
|
52
|
+
v-else
|
|
52
53
|
:size="'video' === hoverType ? 50 : 40"
|
|
53
54
|
/>
|
|
54
|
-
<PodcastIsPlaying v-if="playingPodcast && playerStore.playerVideo" />
|
|
55
55
|
<time
|
|
56
56
|
class="ms-2 me-2"
|
|
57
57
|
:datetime="durationIso"
|
|
@@ -74,9 +74,12 @@ const filterStore = useFilterStore();
|
|
|
74
74
|
|
|
75
75
|
//Computed
|
|
76
76
|
const tagListFiltered = computed(() => {
|
|
77
|
-
|
|
77
|
+
const tags = props.tagList.filter((tag: string) => {
|
|
78
78
|
return !tag.match(/^\[\[.*\]\]$/);
|
|
79
79
|
});
|
|
80
|
+
|
|
81
|
+
// Each tag is only displayed once
|
|
82
|
+
return tags.filter((tag, index) => tags.indexOf(tag) === index);
|
|
80
83
|
});
|
|
81
84
|
const organisationQuery = computed(() => {
|
|
82
85
|
if(filterStore.filterOrgaId){
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
</div>
|
|
97
97
|
</template>
|
|
98
98
|
|
|
99
|
-
<script setup lang="ts">
|
|
99
|
+
<script setup lang="ts" generic="T">
|
|
100
100
|
import { computed, defineAsyncComponent, ref, Ref, watch } from "vue";
|
|
101
101
|
import { useI18n } from "vue-i18n";
|
|
102
102
|
import AsteriskIcon from "vue-material-design-icons/Asterisk.vue";
|
|
@@ -108,37 +108,43 @@ const ClassicPopover = defineAsyncComponent(
|
|
|
108
108
|
);
|
|
109
109
|
|
|
110
110
|
//Props
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
111
|
+
const {
|
|
112
|
+
inModal = false, multiple = false, isDisabled = false, width = "100%",
|
|
113
|
+
maxElement = 50, minSearchLength = 3, noDeselect = true, displayLabel = false,
|
|
114
|
+
allowEmpty = true, optionChosen, maxOptions = null,
|
|
115
|
+
optionCustomTemplating = '', optionSelectedCustomTemplating = ''
|
|
116
|
+
} = defineProps<{
|
|
117
|
+
id?: string;
|
|
118
|
+
label?: string;
|
|
119
|
+
placeholder?: string;
|
|
120
|
+
optionLabel?: string;
|
|
121
|
+
inModal?: boolean;
|
|
122
|
+
multiple?: boolean;
|
|
123
|
+
isDisabled?: boolean;
|
|
124
|
+
width?: string;
|
|
125
|
+
height?: string;
|
|
126
|
+
maxElement?: number;
|
|
127
|
+
minSearchLength?: number;
|
|
128
|
+
/** Currently chosen option */
|
|
129
|
+
optionChosen?: T|Array<T>;
|
|
130
|
+
noDeselect?: boolean;
|
|
131
|
+
optionCustomTemplating?: string;
|
|
132
|
+
optionSelectedCustomTemplating?: string;
|
|
133
|
+
displayLabel?: boolean;
|
|
134
|
+
maxOptions?: number;
|
|
135
|
+
allowEmpty?: boolean;
|
|
136
|
+
textDanger ?:string;
|
|
137
|
+
displayRequired?: boolean;
|
|
138
|
+
popover?: string;
|
|
139
|
+
popoverRelativeClass?: string;
|
|
140
|
+
}>();
|
|
135
141
|
|
|
136
142
|
//Emits
|
|
137
143
|
const emit = defineEmits(["onSearch", "selected", "onClose"]);
|
|
138
144
|
|
|
139
145
|
//Data
|
|
140
|
-
const optionSelected : Ref<
|
|
141
|
-
const options : Ref<Array<
|
|
146
|
+
const optionSelected : Ref<T|T[]>= ref(undefined);
|
|
147
|
+
const options : Ref<Array<T>>= ref([]);
|
|
142
148
|
const remainingElements = ref(0);
|
|
143
149
|
const isLoading = ref(false);
|
|
144
150
|
const searchInput = ref("");
|
|
@@ -149,20 +155,20 @@ const { t } = useI18n();
|
|
|
149
155
|
|
|
150
156
|
//Computed
|
|
151
157
|
const maxOptionsSelected = computed(() => {
|
|
152
|
-
if (
|
|
158
|
+
if (maxOptions !== null && multiple) {
|
|
153
159
|
return (
|
|
154
|
-
(optionSelected.value as Array<
|
|
160
|
+
(optionSelected.value as Array<T>).length >= maxOptions
|
|
155
161
|
);
|
|
156
162
|
}
|
|
157
163
|
return false;
|
|
158
164
|
});
|
|
159
165
|
|
|
160
166
|
//Watch
|
|
161
|
-
watch(()=>
|
|
162
|
-
optionSelected.value =
|
|
167
|
+
watch(()=>optionChosen, () => {
|
|
168
|
+
optionSelected.value = optionChosen;
|
|
163
169
|
}, {deep: true, immediate: true});
|
|
164
170
|
watch(optionSelected, () => {
|
|
165
|
-
if (
|
|
171
|
+
if (noDeselect || null !== optionSelected.value) {
|
|
166
172
|
return;
|
|
167
173
|
}
|
|
168
174
|
emit("selected", undefined);
|
|
@@ -173,7 +179,7 @@ function fakeSearch(): Array<unknown> {
|
|
|
173
179
|
return options.value;
|
|
174
180
|
}
|
|
175
181
|
function onSearch(search?: string): void {
|
|
176
|
-
if (search && search.length <
|
|
182
|
+
if (search && search.length < minSearchLength) {
|
|
177
183
|
return;
|
|
178
184
|
} else if (search) {
|
|
179
185
|
searchInput.value = search;
|
|
@@ -185,20 +191,20 @@ function onClose() {
|
|
|
185
191
|
emit("onClose", searchInput.value);
|
|
186
192
|
searchInput.value = "";
|
|
187
193
|
}
|
|
188
|
-
function afterSearch(optionsFetched: Array<
|
|
194
|
+
function afterSearch(optionsFetched: Array<T>, count: number): void {
|
|
189
195
|
options.value = optionsFetched;
|
|
190
|
-
remainingElements.value = Math.max(0, count -
|
|
196
|
+
remainingElements.value = Math.max(0, count - maxElement);
|
|
191
197
|
isLoading.value = false;
|
|
192
198
|
}
|
|
193
199
|
function onOptionSelected(optionSelected: unknown): void {
|
|
194
200
|
emit("selected", optionSelected);
|
|
195
201
|
}
|
|
196
202
|
function onOptionDeselect(event: unknown): void {
|
|
197
|
-
if (!
|
|
203
|
+
if (!multiple) {
|
|
198
204
|
return;
|
|
199
205
|
}
|
|
200
206
|
if (
|
|
201
|
-
!
|
|
207
|
+
!allowEmpty &&
|
|
202
208
|
0 === (optionSelected.value as Array<unknown>).length
|
|
203
209
|
) {
|
|
204
210
|
(optionSelected.value as Array<unknown>).push(event);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Library of icons to use in the application
|
|
3
|
+
* Icons should be defined here, then used with their alias.
|
|
4
|
+
* This will allow for better consistency, and easily changing icons.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import SquareEditOutline from 'vue-material-design-icons/SquareEditOutline.vue';
|
|
8
|
+
import TrashCan from 'vue-material-design-icons/TrashCan.vue';
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
Delete: TrashCan,
|
|
12
|
+
Edit: SquareEditOutline
|
|
13
|
+
}
|
|
@@ -82,6 +82,13 @@ const iconComponent = computed(() => {
|
|
|
82
82
|
display: flex;
|
|
83
83
|
flex-direction: column;
|
|
84
84
|
align-self: center;
|
|
85
|
+
|
|
86
|
+
&:deep(p) {
|
|
87
|
+
margin: 0 !important;
|
|
88
|
+
&:not(:first-child) {
|
|
89
|
+
margin-top: 8px !important;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
$types: (
|
|
@@ -103,4 +110,4 @@ const iconComponent = computed(() => {
|
|
|
103
110
|
}
|
|
104
111
|
}
|
|
105
112
|
}
|
|
106
|
-
</style>
|
|
113
|
+
</style>
|