@saooti/octopus-sdk 41.1.15 → 41.2.1
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 +44 -0
- package/index.ts +29 -2
- 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 +40 -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/EmissionList.vue +8 -2
- package/src/components/display/filter/AdvancedSearch.vue +83 -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 +14 -288
- package/src/router/routes.ts +236 -0
- package/src/router/utils.ts +43 -2
- package/src/stores/class/general/emission.ts +8 -2
- package/src/style/_variables.scss +3 -0
- package/src/style/bootstrap.scss +5 -0
|
@@ -12,6 +12,7 @@ export const useErrorHandler = ()=>{
|
|
|
12
12
|
if (undefined === authStore.authOrgaId) {
|
|
13
13
|
window.location.href = window.location.origin + "/sso/login";
|
|
14
14
|
} else {
|
|
15
|
+
console.error(error);
|
|
15
16
|
router.push({
|
|
16
17
|
path: "/main/pub/error",
|
|
17
18
|
});
|
|
@@ -19,7 +20,7 @@ export const useErrorHandler = ()=>{
|
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
return {
|
|
23
24
|
handle403
|
|
24
|
-
|
|
25
|
+
}
|
|
25
26
|
}
|
|
@@ -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?: string|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].flat(),
|
|
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>
|
|
@@ -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,24 @@
|
|
|
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
|
+
:organisation-id="organisationId"
|
|
48
|
+
:groups="emissionGroups"
|
|
49
|
+
@update:groups="updateEmissionGroupFilter"
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
35
53
|
<!-- Date -->
|
|
36
54
|
<DateFilter
|
|
37
55
|
:is-emission="isEmission"
|
|
@@ -117,7 +135,7 @@ import { useAuthStore } from "../../../stores/AuthStore";
|
|
|
117
135
|
import { useFilterStore } from "../../../stores/FilterStore";
|
|
118
136
|
import { useRubriquesFilterParam } from "../../composable/route/useRubriquesFilterParam";
|
|
119
137
|
import { RubriquageFilter } from "@/stores/class/rubrique/rubriquageFilter";
|
|
120
|
-
import { defineAsyncComponent, ref, computed, watch } from "vue";
|
|
138
|
+
import { defineAsyncComponent, ref, computed, watch, onMounted } from "vue";
|
|
121
139
|
import { useGeneralStore } from "../../../stores/GeneralStore";
|
|
122
140
|
import { useI18n } from "vue-i18n";
|
|
123
141
|
const MonetizableFilter = defineAsyncComponent(
|
|
@@ -141,27 +159,34 @@ const ClassicCheckbox = defineAsyncComponent(
|
|
|
141
159
|
const DateFilter = defineAsyncComponent(() => import("./DateFilter.vue"));
|
|
142
160
|
const SearchOrder = defineAsyncComponent(() => import("./SearchOrder.vue"));
|
|
143
161
|
import { ROUTE_PARAMS } from '../../composable/route/types';
|
|
162
|
+
import { EmissionGroup, groupsApi } from "../../../api/groupsApi";
|
|
163
|
+
import EmissionGroupChooser from "../emission/EmissionGroupChooser.vue";
|
|
144
164
|
|
|
145
165
|
//Props
|
|
146
|
-
const props = defineProps
|
|
147
|
-
organisationId
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
166
|
+
const props = withDefaults(defineProps<{
|
|
167
|
+
organisationId?: string;
|
|
168
|
+
/** Indicates that the filters apply to emissions */
|
|
169
|
+
isEmission?: boolean;
|
|
170
|
+
includeHidden?: boolean;
|
|
171
|
+
sort?: string;
|
|
172
|
+
onlyVideo?: boolean;
|
|
173
|
+
monetisable?: string;
|
|
174
|
+
iabId?: number;
|
|
175
|
+
searchPattern?: string;
|
|
176
|
+
fromDate?: string;
|
|
177
|
+
toDate?: string;
|
|
178
|
+
validity?: string;
|
|
158
179
|
/** The filter on beneficiaries */
|
|
159
|
-
beneficiaries
|
|
160
|
-
rubriqueFilter
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
180
|
+
beneficiaries?: Array<string>;
|
|
181
|
+
rubriqueFilter?: Array<RubriquageFilter>;
|
|
182
|
+
/** The filter on groups */
|
|
183
|
+
emissionGroups?: Array<EmissionGroup>;
|
|
184
|
+
}>(), {
|
|
185
|
+
sort: "DATE",
|
|
186
|
+
monetisable: "UNDEFINED",
|
|
187
|
+
searchPattern: "",
|
|
188
|
+
validity: "true"
|
|
189
|
+
});
|
|
165
190
|
|
|
166
191
|
//Emits
|
|
167
192
|
const emit = defineEmits([
|
|
@@ -174,13 +199,14 @@ const emit = defineEmits([
|
|
|
174
199
|
"update:validity",
|
|
175
200
|
"update:rubriqueFilter",
|
|
176
201
|
"update:onlyVideo",
|
|
177
|
-
"update:beneficiaries"
|
|
202
|
+
"update:beneficiaries",
|
|
203
|
+
"update:emission-groups"
|
|
178
204
|
]);
|
|
179
205
|
|
|
180
206
|
//Data
|
|
181
207
|
const showFilters = ref(false);
|
|
182
208
|
const firstLoaded = ref(false);
|
|
183
|
-
|
|
209
|
+
const showEmissionGroups = ref(false);
|
|
184
210
|
|
|
185
211
|
//Composables
|
|
186
212
|
const { t } = useI18n();
|
|
@@ -190,6 +216,13 @@ const generalStore = useGeneralStore();
|
|
|
190
216
|
const filterStore = useFilterStore();
|
|
191
217
|
const authStore = useAuthStore();
|
|
192
218
|
|
|
219
|
+
onMounted(async() => {
|
|
220
|
+
// Only show emission groups if there are some
|
|
221
|
+
const nbGroups = await groupsApi.count({
|
|
222
|
+
organisationIds: [props.organisationId]
|
|
223
|
+
});
|
|
224
|
+
showEmissionGroups.value = nbGroups > 0;
|
|
225
|
+
});
|
|
193
226
|
|
|
194
227
|
//Computed
|
|
195
228
|
const organisationRight = computed(() => isEditRights(props.organisationId));
|
|
@@ -205,6 +238,7 @@ const isSelectValidity = computed(() => {
|
|
|
205
238
|
props.includeHidden
|
|
206
239
|
);
|
|
207
240
|
});
|
|
241
|
+
|
|
208
242
|
/** The beneficiaries filter is only displayed if beneficiaries are enabled */
|
|
209
243
|
const beneficiariesEnabled = computed(() => {
|
|
210
244
|
return authStore.authOrganisation.attributes['beneficiaries.enabled'] === 'true';
|
|
@@ -288,11 +322,37 @@ function updateRubriquageFilter(value: Array<RubriquageFilter>) {
|
|
|
288
322
|
filterRubriques = { rubriquesId: undefined };
|
|
289
323
|
}
|
|
290
324
|
updateRouteParamAdvanced({
|
|
291
|
-
|
|
325
|
+
r: valueString.length ? valueString : undefined,
|
|
292
326
|
...filterRubriques,
|
|
293
327
|
});
|
|
294
328
|
}
|
|
295
329
|
|
|
330
|
+
/** Manage the checkbox enabling filtering on groups */
|
|
331
|
+
const emissionGroupCheckbox = computed({
|
|
332
|
+
get(): boolean {
|
|
333
|
+
return props.emissionGroups !== undefined && props.emissionGroups !== null;
|
|
334
|
+
},
|
|
335
|
+
set(value: boolean): void {
|
|
336
|
+
if (value) {
|
|
337
|
+
updateEmissionGroupFilter([]);
|
|
338
|
+
} else {
|
|
339
|
+
updateEmissionGroupFilter(undefined);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
/** Update selected groups */
|
|
345
|
+
function updateEmissionGroupFilter(groups: Array<EmissionGroup>|undefined): void {
|
|
346
|
+
if (groups !== undefined && groups.length === 0) {
|
|
347
|
+
emit('update:emission-groups', []);
|
|
348
|
+
} else {
|
|
349
|
+
updateRouteParamAdvanced({
|
|
350
|
+
[ROUTE_PARAMS.EmissionGroups]: groups?.map(g => g.groupId)
|
|
351
|
+
});
|
|
352
|
+
emit('update:emission-groups', groups);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
296
356
|
/** Update the beneficiaries filter */
|
|
297
357
|
function updateBeneficiaries(value: string[]|undefined): void {
|
|
298
358
|
emit('update:beneficiaries', value);
|
|
@@ -350,4 +410,4 @@ function clickShowFilters(): void {
|
|
|
350
410
|
overflow: hidden;
|
|
351
411
|
}
|
|
352
412
|
}
|
|
353
|
-
</style>
|
|
413
|
+
</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>
|