@saooti/octopus-sdk 41.12.1-beta2 → 41.12.1-beta3
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 +6 -0
- package/eslint-config.mjs +3 -0
- package/package.json +1 -1
- package/src/api/podcastApi.ts +0 -1
- package/src/components/display/emission/EmissionInlineList.vue +17 -2
- package/src/components/display/list/PaginateSection.vue +11 -7
- package/src/components/display/list/SwiperList.vue +10 -6
- package/src/components/display/podcasts/PodcastList.vue +1 -0
- package/src/components/display/podcasts/PodcastPresentationList.vue +86 -46
- package/src/components/display/podcasts/PodcastRawTranscript.vue +0 -1
- package/src/components/layouts/PresentationItem.vue +2 -1
- package/src/components/layouts/PresentationLayout.vue +3 -3
- package/src/style/_variables.scss +6 -0
- package/src/style/bootstrap.scss +2 -1
- package/tests/components/layouts/PresentationItem.spec.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -12,10 +12,16 @@
|
|
|
12
12
|
|
|
13
13
|
- Correction d'une anomalie de chargement du style de la modale utilisée par
|
|
14
14
|
`ClassicNotifications`
|
|
15
|
+
- Ajout mode alternatif pour la récupération des épisodes dans
|
|
16
|
+
`PodcastPresentationList`
|
|
17
|
+
- Affichage des infos additionelles dans PresentationItem même sans description
|
|
15
18
|
|
|
16
19
|
**Misc**
|
|
17
20
|
|
|
18
21
|
- Suppression de la marge entre le titre et le contenu pour `PresentationLayout`
|
|
22
|
+
- Uniformisation espacement pour composants PM
|
|
23
|
+
- Mise à jour règles eslint
|
|
24
|
+
- Ajustement couleurs pour boutons secondaires
|
|
19
25
|
|
|
20
26
|
## 41.12.0 (13/07/2026)
|
|
21
27
|
|
package/eslint-config.mjs
CHANGED
|
@@ -45,6 +45,9 @@ export default typescriptEslint.config(
|
|
|
45
45
|
// Number of attributes per line (increase because sometimes two is not a lot)
|
|
46
46
|
"vue/max-attributes-per-line": ['warn', { singleline: 2 } ],
|
|
47
47
|
|
|
48
|
+
// Disable default values required for props
|
|
49
|
+
"vue/require-default-prop": ['off'],
|
|
50
|
+
|
|
48
51
|
"@typescript-eslint/no-unused-vars": "warn"
|
|
49
52
|
}
|
|
50
53
|
}
|
package/package.json
CHANGED
package/src/api/podcastApi.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Podcast, PodcastProcessingStatus, PodcastType, SimplifiedPodcast } from '../stores/class/general/podcast';
|
|
2
2
|
import { ListClassicReturn } from '../stores/class/general/listReturn';
|
|
3
|
-
import { useAuthStore } from '../stores/AuthStore';
|
|
4
3
|
import classicApi from './classicApi';
|
|
5
4
|
import { ModuleApi } from './apiConnection';
|
|
6
5
|
import { unique } from '../helper/arrayHelper';
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="d-flex flex-column list-episode">
|
|
3
|
+
<h2 v-if="title" class="mb-0">
|
|
4
|
+
{{ title }}
|
|
5
|
+
</h2>
|
|
3
6
|
<ClassicLoading
|
|
4
7
|
:loading-text="loading ? t('Loading emissions ...') : undefined"
|
|
5
8
|
/>
|
|
6
9
|
<SwiperList
|
|
7
10
|
v-if="(displayRubriquage && rubriques) || !(displayRubriquage && loaded)"
|
|
8
|
-
:size-item-overload="
|
|
11
|
+
:size-item-overload="sizeItemOverload"
|
|
9
12
|
:list-object="allEmissions"
|
|
10
13
|
>
|
|
11
14
|
<template #octopusSlide="{ option }">
|
|
12
15
|
<EmissionPresentationItem
|
|
13
16
|
v-if="emissionDisplay === 'simple'"
|
|
14
17
|
:emission="option"
|
|
15
|
-
class="
|
|
18
|
+
class="inline-list-element"
|
|
16
19
|
is-description
|
|
17
20
|
:is-vertical="emissionVertical"
|
|
18
21
|
/>
|
|
@@ -76,6 +79,8 @@ const props = defineProps<{
|
|
|
76
79
|
rubriqueId?: number;
|
|
77
80
|
/** Filter on rubriquage */
|
|
78
81
|
rubriquageId?: number;
|
|
82
|
+
/** Title of the section */
|
|
83
|
+
title?: string;
|
|
79
84
|
}>();
|
|
80
85
|
|
|
81
86
|
|
|
@@ -93,6 +98,16 @@ const {handle403} = useErrorHandler();
|
|
|
93
98
|
//Computed
|
|
94
99
|
const displayRubriquage = computed(() => state.emissionsPage.rubriquage);
|
|
95
100
|
|
|
101
|
+
const sizeItemOverload = computed(() => {
|
|
102
|
+
if (props.itemSize) {
|
|
103
|
+
return props.itemSize;
|
|
104
|
+
} else if (props.emissionDisplay === 'simple') {
|
|
105
|
+
return 25;
|
|
106
|
+
} else {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
|
|
96
111
|
onMounted(()=>{
|
|
97
112
|
fetchNext();
|
|
98
113
|
if (displayRubriquage.value) {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<button
|
|
8
8
|
v-for="paginateButton in buttonsLeft"
|
|
9
9
|
:key="paginateButton.title"
|
|
10
|
-
class="btn"
|
|
10
|
+
class="btn btn-paginate"
|
|
11
11
|
:title="paginateButton.title"
|
|
12
12
|
:disabled="paginateButton.disabled"
|
|
13
13
|
@click="paginateButton.action"
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
</svg>
|
|
24
24
|
</button>
|
|
25
25
|
<template v-for="pageNumber in pagination" :key="pageNumber">
|
|
26
|
-
<span v-if="null === pageNumber" class="btn btn-min-width"> ... </span>
|
|
26
|
+
<span v-if="null === pageNumber" class="btn btn-min-width btn-paginate"> ... </span>
|
|
27
27
|
<button
|
|
28
28
|
v-else
|
|
29
|
-
class="btn btn-min-width"
|
|
29
|
+
class="btn btn-min-width btn-paginate"
|
|
30
30
|
:class="{ active: page === pageNumber - 1 }"
|
|
31
31
|
@click="changeFirst((pageNumber - 1) * rowsPerPage)"
|
|
32
32
|
>
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
<button
|
|
37
37
|
v-for="paginateButton in buttonsRight"
|
|
38
38
|
:key="paginateButton.title"
|
|
39
|
-
class="btn btn-min-width"
|
|
39
|
+
class="btn btn-min-width btn-paginate"
|
|
40
40
|
:title="paginateButton.title"
|
|
41
41
|
:disabled="paginateButton.disabled"
|
|
42
42
|
@click="paginateButton.action"
|
|
@@ -176,6 +176,7 @@ function changeFirst(newFirst: number) {
|
|
|
176
176
|
emit("update:first", newFirst);
|
|
177
177
|
}
|
|
178
178
|
</script>
|
|
179
|
+
|
|
179
180
|
<style lang="scss">
|
|
180
181
|
|
|
181
182
|
.octopus-app {
|
|
@@ -189,15 +190,18 @@ function changeFirst(newFirst: number) {
|
|
|
189
190
|
padding: 0.5rem 0;
|
|
190
191
|
z-index: 10;
|
|
191
192
|
|
|
192
|
-
.btn {
|
|
193
|
+
.btn.btn-paginate {
|
|
193
194
|
border-radius: 0;
|
|
195
|
+
color: var(--octopus-btn-paginate-fg);
|
|
196
|
+
background: var(--octopus-btn-paginate-bg);
|
|
194
197
|
|
|
195
198
|
&.active {
|
|
196
|
-
|
|
199
|
+
color: var(--octopus-btn-paginate-active-fg);
|
|
200
|
+
background: var(--octopus-btn-paginate-active-bg);
|
|
197
201
|
}
|
|
198
202
|
}
|
|
199
203
|
}
|
|
200
|
-
|
|
204
|
+
|
|
201
205
|
.module-box .paginate-fixed,
|
|
202
206
|
.octopus-modal .paginate-fixed,
|
|
203
207
|
.octopus-accordion .paginate-fixed {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<swiper
|
|
5
5
|
:key="manualReload"
|
|
6
6
|
:slides-per-view="numberItem"
|
|
7
|
-
:space-between="
|
|
7
|
+
:space-between="gapPx"
|
|
8
8
|
:loop="loop"
|
|
9
9
|
:slides-offset-before="offsetSwiper"
|
|
10
10
|
:slides-offset-after="offsetSwiper"
|
|
@@ -47,7 +47,8 @@ const props = defineProps({
|
|
|
47
47
|
sizeItemOverload: { default: undefined, type: Number },
|
|
48
48
|
})
|
|
49
49
|
|
|
50
|
-
//Data
|
|
50
|
+
//Data
|
|
51
|
+
const gapPx = 10;
|
|
51
52
|
const manualReload = ref(0);
|
|
52
53
|
const numberItem = ref(5);
|
|
53
54
|
const offsetSwiper = ref(0);
|
|
@@ -73,7 +74,10 @@ const sizeItem = computed(() => {
|
|
|
73
74
|
? state.generalParameters.podcastItem
|
|
74
75
|
: 13.5;
|
|
75
76
|
});
|
|
76
|
-
const itemRecalculizedSize = computed(() =>
|
|
77
|
+
const itemRecalculizedSize = computed(() => {
|
|
78
|
+
const totalGap = gapPx * Math.max(numberItem.value - 1, 0);
|
|
79
|
+
return (widthSwiperUsable.value - totalGap) / numberItem.value;
|
|
80
|
+
});
|
|
77
81
|
|
|
78
82
|
/** Indicates that the swiper should loop */
|
|
79
83
|
const loop = computed((): boolean => {
|
|
@@ -119,10 +123,10 @@ function onWindowResize(){
|
|
|
119
123
|
const el = rootRef?.value as HTMLElement;
|
|
120
124
|
if (!el) return;
|
|
121
125
|
widthSwiperUsable.value =el.offsetWidth - offsetSwiper.value * 2;
|
|
122
|
-
const
|
|
126
|
+
const itemSizePx = domHelper.convertRemToPixels(sizeItem.value + 0.5);
|
|
123
127
|
numberItem.value = Math.max(
|
|
124
128
|
1,
|
|
125
|
-
Math.floor(widthSwiperUsable.value /
|
|
129
|
+
Math.floor((widthSwiperUsable.value + gapPx) / (itemSizePx + gapPx)),
|
|
126
130
|
);
|
|
127
131
|
itemSizeWithoutRecalculed.value =el.offsetWidth / numberItem.value;
|
|
128
132
|
}
|
|
@@ -153,7 +157,7 @@ function slideChange() {
|
|
|
153
157
|
);
|
|
154
158
|
wrapper.style.transform =
|
|
155
159
|
"translate3d(" +
|
|
156
|
-
(nbTransformItems * itemRecalculizedSize.value + offsetSwiper.value) +
|
|
160
|
+
(nbTransformItems * (itemRecalculizedSize.value + gapPx) + offsetSwiper.value) +
|
|
157
161
|
"px, 0px, 0px)";
|
|
158
162
|
}
|
|
159
163
|
</script>
|
|
@@ -57,14 +57,34 @@ import { podcastApi, PodcastSort } from "../../../api/podcastApi";
|
|
|
57
57
|
import { usePresentationItem } from "../../composable/usePresentationItem";
|
|
58
58
|
|
|
59
59
|
//Props
|
|
60
|
-
const props = defineProps
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
const props = withDefaults(defineProps<{
|
|
61
|
+
/**
|
|
62
|
+
* ID of the organisation
|
|
63
|
+
*/
|
|
64
|
+
organisationId?: string;
|
|
65
|
+
/**
|
|
66
|
+
* Title of the section
|
|
67
|
+
*/
|
|
68
|
+
title?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Link to the "more" section
|
|
71
|
+
*/
|
|
72
|
+
href?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Label for the "more" button
|
|
75
|
+
*/
|
|
76
|
+
buttonText?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Display podcasts exclusively from these rubriques
|
|
79
|
+
*/
|
|
80
|
+
rubriquesId?: Array<number>;
|
|
81
|
+
/**
|
|
82
|
+
* Mode of retrieval for podcasts
|
|
83
|
+
*/
|
|
84
|
+
retrievalMode?: 'by-emission'|'any';
|
|
85
|
+
}>(), {
|
|
86
|
+
retrievalMode: 'by-emission'
|
|
87
|
+
});
|
|
68
88
|
|
|
69
89
|
//Data
|
|
70
90
|
const loading = ref(true);
|
|
@@ -92,47 +112,16 @@ watch(podcasts, async () => {
|
|
|
92
112
|
async function fetchNext(): Promise<void> {
|
|
93
113
|
loading.value = true;
|
|
94
114
|
try {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
first: 0,
|
|
101
|
-
size: 5,
|
|
102
|
-
organisationId: props.organisationId,
|
|
103
|
-
sort: "LAST_PODCAST_DESC",
|
|
104
|
-
rubriqueId: props.rubriquesId
|
|
105
|
-
},
|
|
106
|
-
specialTreatement: true,
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
const promises: Array<Promise<SimplifiedPodcast>> = [];
|
|
110
|
-
|
|
111
|
-
for (let i = 0; i < emissions.result.length; i++) {
|
|
112
|
-
promises.push(podcastApi.search({
|
|
113
|
-
first: 0,
|
|
114
|
-
size: 1,
|
|
115
|
-
organisationId: [props.organisationId],
|
|
116
|
-
emissionId: [emissions.result[i].emissionId],
|
|
117
|
-
sort: PodcastSort.DATE,
|
|
118
|
-
rubriqueId: props.rubriquesId
|
|
119
|
-
}).then(r => r.result[0]));
|
|
115
|
+
let func: () => Promise<Array<Podcast>>;
|
|
116
|
+
if (props.retrievalMode === 'any') {
|
|
117
|
+
func = fetchPodcasts;
|
|
118
|
+
} else {
|
|
119
|
+
func = fetchPodcastsByEmission;
|
|
120
120
|
}
|
|
121
|
-
|
|
122
|
-
// Retrieve the podcasts for these emissions
|
|
123
|
-
const data = await Promise.all(promises);
|
|
124
|
-
|
|
125
|
-
podcasts.value = podcasts.value.concat(
|
|
126
|
-
data.filter((em: SimplifiedPodcast | null) => null !== em && undefined !== em).map(p => {
|
|
127
|
-
// Get emission from podcast
|
|
128
|
-
const emission = emissions.result.find(e => e.emissionId === p.emissionId);
|
|
129
|
-
// Create full podcast from simplified + emission
|
|
130
|
-
return simplifiedToFull(p, emission.orga, emission);
|
|
131
|
-
})
|
|
132
|
-
);
|
|
121
|
+
const result = await func();
|
|
133
122
|
|
|
134
123
|
// Sort podcasts by pub date so that the most recent one is focused
|
|
135
|
-
podcasts.value.sort((p1, p2) => {
|
|
124
|
+
podcasts.value = result.sort((p1, p2) => {
|
|
136
125
|
return new Date(p2.pubDate).getTime() - new Date(p1.pubDate).getTime();
|
|
137
126
|
});
|
|
138
127
|
|
|
@@ -145,6 +134,57 @@ async function fetchNext(): Promise<void> {
|
|
|
145
134
|
loading.value = false;
|
|
146
135
|
}
|
|
147
136
|
|
|
137
|
+
async function fetchPodcasts(): Promise<Array<Podcast>> {
|
|
138
|
+
const response = await podcastApi.searchFull({
|
|
139
|
+
first: 0,
|
|
140
|
+
size: 5,
|
|
141
|
+
organisationId: [props.organisationId],
|
|
142
|
+
sort: PodcastSort.DATE,
|
|
143
|
+
rubriqueId: props.rubriquesId
|
|
144
|
+
}, true);
|
|
145
|
+
|
|
146
|
+
return response.result;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function fetchPodcastsByEmission(): Promise<Array<Podcast>> {
|
|
150
|
+
// Retrieve latest emissions
|
|
151
|
+
const emissions = await classicApi.fetchData<ListClassicReturn<Emission>>({
|
|
152
|
+
api: 0,
|
|
153
|
+
path: "emission/search",
|
|
154
|
+
parameters: {
|
|
155
|
+
first: 0,
|
|
156
|
+
size: 5,
|
|
157
|
+
organisationId: props.organisationId,
|
|
158
|
+
sort: "LAST_PODCAST_DESC",
|
|
159
|
+
rubriqueId: props.rubriquesId
|
|
160
|
+
},
|
|
161
|
+
specialTreatement: true
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const promises: Array<Promise<SimplifiedPodcast>> = [];
|
|
165
|
+
|
|
166
|
+
for (let i = 0; i < emissions.result.length; i++) {
|
|
167
|
+
promises.push(podcastApi.search({
|
|
168
|
+
first: 0,
|
|
169
|
+
size: 1,
|
|
170
|
+
organisationId: [props.organisationId],
|
|
171
|
+
emissionId: [emissions.result[i].emissionId],
|
|
172
|
+
sort: PodcastSort.DATE,
|
|
173
|
+
rubriqueId: props.rubriquesId
|
|
174
|
+
}, true).then(r => r.result[0]));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Retrieve the podcasts for these emissions
|
|
178
|
+
const data = await Promise.all(promises);
|
|
179
|
+
|
|
180
|
+
return data.filter((em: SimplifiedPodcast | null) => null !== em && undefined !== em).map(p => {
|
|
181
|
+
// Get emission from podcast
|
|
182
|
+
const emission = emissions.result.find(e => e.emissionId === p.emissionId);
|
|
183
|
+
// Create full podcast from simplified + emission
|
|
184
|
+
return simplifiedToFull(p, emission.orga, emission);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
148
188
|
function route(podcast: Podcast): RouteLocationRaw {
|
|
149
189
|
return {
|
|
150
190
|
name: 'podcast',
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
{{ name }}
|
|
45
45
|
</div>
|
|
46
46
|
<div
|
|
47
|
-
v-if="!isPhone && description"
|
|
47
|
+
v-if="!isPhone && (description || additionalInfo)"
|
|
48
48
|
ref="descriptionItemContainer"
|
|
49
49
|
class="element-description htms-wysiwyg-content mt-0"
|
|
50
50
|
>
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
</div>
|
|
60
60
|
<!-- eslint-disable vue/no-v-html -->
|
|
61
61
|
<div
|
|
62
|
+
v-if="description"
|
|
62
63
|
ref="descriptionItem"
|
|
63
64
|
v-html="urlify(description || '')"
|
|
64
65
|
/>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
A simple layout to display 5 elements over 3 columns
|
|
3
3
|
-->
|
|
4
4
|
<template>
|
|
5
|
-
<div class="d-flex flex-column
|
|
5
|
+
<div class="d-flex flex-column py-3">
|
|
6
6
|
<h2 v-if="title">
|
|
7
7
|
{{ title }}
|
|
8
8
|
</h2>
|
|
@@ -98,10 +98,10 @@ defineProps<{
|
|
|
98
98
|
|
|
99
99
|
.column {
|
|
100
100
|
flex-shrink: 0;
|
|
101
|
-
width: calc((100% - 420px) / 2);
|
|
101
|
+
width: calc((100% - 420px - 1rem) / 2);
|
|
102
102
|
|
|
103
103
|
@media (width <= 1550px) {
|
|
104
|
-
width: calc((100% - 420px));
|
|
104
|
+
width: calc((100% - 420px - 1rem));
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
@media (width <= 960px) {
|
|
@@ -46,11 +46,17 @@
|
|
|
46
46
|
// Buttons
|
|
47
47
|
--octopus-btn-primary-bg: var(--octopus-primary);
|
|
48
48
|
--octopus-btn-primary-fg: var(--octopus-color-on-primary);
|
|
49
|
+
--octopus-btn-secondary-bg: var(--octopus-secondary);
|
|
50
|
+
--octopus-btn-secondary-fg: black;
|
|
49
51
|
--octopus-btn-play-bg: var(--octopus-primary-less-transparent);
|
|
50
52
|
--octopus-btn-play-fg: white;
|
|
51
53
|
--octopus-btn-play-radius: var(--octopus-border-radius);
|
|
52
54
|
--octopus-btn-social-bg: var(--octopus-secondary);
|
|
53
55
|
--octopus-btn-social-fg: var(--octopus-primary);
|
|
56
|
+
--octopus-btn-paginate-fg: var(--octopus-btn-secondary-fg);
|
|
57
|
+
--octopus-btn-paginate-bg: var(--octopus-btn-secondary-bg);
|
|
58
|
+
--octopus-btn-paginate-active-fg: var(--octopus-btn-paginate-fg);
|
|
59
|
+
--octopus-btn-paginate-active-bg: var(--octopus-primary-more-transparent);
|
|
54
60
|
|
|
55
61
|
// Player
|
|
56
62
|
// Color for the transcript background
|
package/src/style/bootstrap.scss
CHANGED
|
@@ -88,7 +88,8 @@ input:not([class^="vs__"]), button:not([class^="vs__"]), select:not([class^="vs_
|
|
|
88
88
|
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
|
89
89
|
font-size:.7rem;
|
|
90
90
|
border-radius: var(--octopus-border-radius);
|
|
91
|
-
|
|
91
|
+
color: var(--octopus-btn-secondary-fg);
|
|
92
|
+
background: var(--octopus-btn-secondary-bg);
|
|
92
93
|
text-decoration: none !important;
|
|
93
94
|
white-space: nowrap;
|
|
94
95
|
border-width: 0;
|
|
@@ -28,9 +28,9 @@ describe('PresentationItem', () => {
|
|
|
28
28
|
expect(wrapper.find('.text-secondary').exists()).toBe(false);
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
it('
|
|
31
|
+
it('renders each additionalInfo even when there is no description', async () => {
|
|
32
32
|
const wrapper = await mount({ additionalInfo: ['Saooti'] });
|
|
33
|
-
expect(wrapper.find('.text-secondary').exists()).toBe(
|
|
33
|
+
expect(wrapper.find('.text-secondary').exists()).toBe(true);
|
|
34
34
|
});
|
|
35
35
|
});
|
|
36
36
|
});
|