@saooti/octopus-sdk 41.0.14 → 41.0.16
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/index.ts +7 -1
- package/package.json +1 -1
- package/src/components/display/playlist/PodcastList.vue +12 -1
- package/src/components/display/podcasts/PodcastImage.vue +28 -7
- package/src/components/display/podcasts/PodcastItem.vue +4 -1
- package/src/components/display/podcasts/PodcastList.vue +9 -1
- package/src/components/display/podcasts/PodcastPlayButton.vue +92 -20
- package/src/components/form/ClassicCheckbox.vue +2 -2
- package/src/components/form/ClassicInputText.vue +21 -9
- package/src/components/form/ClassicSelect.vue +29 -29
- package/src/components/misc/ClassicAccordion.vue +2 -2
- package/src/components/misc/ClassicAlert.vue +98 -0
- package/src/components/misc/ClassicHelpButton.vue +50 -0
- package/src/components/misc/FooterSection.vue +2 -3
- package/src/components/pages/PodcastPage.vue +4 -0
- package/src/router/utils.ts +4 -4
- package/src/stores/AuthStore.ts +2 -2
- package/src/stores/FilterStore.ts +2 -2
- package/src/stores/class/general/organisation.ts +2 -2
- package/typings/index.d.ts +1 -0
package/index.ts
CHANGED
|
@@ -38,6 +38,8 @@ export const getClassicModal = () => import("./src/components/misc/modal/Classic
|
|
|
38
38
|
export const getClassicLazy = () => import("./src/components/misc/ClassicLazy.vue");
|
|
39
39
|
export const getContractPreviewModal = () => import("./src/components/misc/modal/ContractPreviewModal.vue");
|
|
40
40
|
export const getClassicModalInBody = () => import("./src/components/misc/modal/ClassicModalInBody.vue");
|
|
41
|
+
export const getClassicHelpButton = () => import("./src/components/misc/ClassicHelpButton.vue");
|
|
42
|
+
export const getClassicAlert = () => import("./src/components/misc/ClassicAlert.vue");
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
//Display
|
|
@@ -151,6 +153,9 @@ export const getXIcon = () => import("./src/components/icons/XIcon.vue");
|
|
|
151
153
|
// Routing
|
|
152
154
|
import { setupRouter } from './src/router/utils';
|
|
153
155
|
|
|
156
|
+
// Types
|
|
157
|
+
import { type SelectOption } from "./src/components/form/ClassicSelect.vue";
|
|
158
|
+
|
|
154
159
|
export {
|
|
155
160
|
useResizePhone,
|
|
156
161
|
useTagOf,
|
|
@@ -182,5 +187,6 @@ export {
|
|
|
182
187
|
deepEqual,
|
|
183
188
|
downloadHelper,
|
|
184
189
|
displayHelper,
|
|
185
|
-
setupRouter
|
|
190
|
+
setupRouter,
|
|
191
|
+
SelectOption,
|
|
186
192
|
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Component displaying a list of podcasts IN A PLAYLIST
|
|
3
|
+
Do not confuse this with PodcastList from podcasts
|
|
4
|
+
-->
|
|
1
5
|
<template>
|
|
2
6
|
<div>
|
|
3
7
|
<h3 class="mb-3 align-self-baseline">
|
|
4
8
|
{{ titleList }}
|
|
5
9
|
</h3>
|
|
10
|
+
|
|
6
11
|
<ClassicSearch
|
|
7
12
|
v-if="!loading && notEmptyPlaylist"
|
|
8
13
|
v-model:text-init="searchPattern"
|
|
@@ -10,6 +15,7 @@
|
|
|
10
15
|
id-search="podcast-list-search"
|
|
11
16
|
:label="t('Search')"
|
|
12
17
|
/>
|
|
18
|
+
|
|
13
19
|
<ListPaginate
|
|
14
20
|
id="podcastPlaylistListPaginate"
|
|
15
21
|
v-model:first="dfirst"
|
|
@@ -38,7 +44,12 @@
|
|
|
38
44
|
:key="p.podcastId"
|
|
39
45
|
:min-height="410"
|
|
40
46
|
>
|
|
41
|
-
<PodcastItem
|
|
47
|
+
<PodcastItem
|
|
48
|
+
v-if="0 !== p.podcastId"
|
|
49
|
+
:podcast="p"
|
|
50
|
+
:in-list="true"
|
|
51
|
+
/>
|
|
52
|
+
|
|
42
53
|
<template #preview>
|
|
43
54
|
<router-link
|
|
44
55
|
:to="{
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Component to display the image of a podcast.
|
|
3
|
+
May also display additional information, for example when an error occured
|
|
4
|
+
when processing the file (using PodcastPlayButton).
|
|
5
|
+
-->
|
|
1
6
|
<template>
|
|
2
7
|
<div
|
|
3
8
|
v-if="podcast"
|
|
@@ -16,10 +21,9 @@
|
|
|
16
21
|
height="270"
|
|
17
22
|
aria-hidden="true"
|
|
18
23
|
alt=""
|
|
19
|
-
|
|
20
24
|
class="img-box img-box-podcast"
|
|
21
25
|
:title="t('Episode name image', { name: podcast.title })"
|
|
22
|
-
|
|
26
|
+
>
|
|
23
27
|
</router-link>
|
|
24
28
|
<div
|
|
25
29
|
v-if="state.generalParameters.podcastmaker"
|
|
@@ -43,6 +47,7 @@
|
|
|
43
47
|
:podcast="podcast"
|
|
44
48
|
:hide-play="hidePlay"
|
|
45
49
|
:fetch-conference="fetchConference"
|
|
50
|
+
:in-list="inList"
|
|
46
51
|
/>
|
|
47
52
|
<button
|
|
48
53
|
v-if="displayDescription && isMobile"
|
|
@@ -73,6 +78,8 @@ const props = defineProps({
|
|
|
73
78
|
arrowDirection: { default: "up", type: String },
|
|
74
79
|
isAnimatorLive: { default: false, type: Boolean },
|
|
75
80
|
fetchConference: { default: undefined, type: Object as () => Conference },
|
|
81
|
+
/** Indicates that the podcast is displayed in a list */
|
|
82
|
+
inList: { default: false, type: Boolean }
|
|
76
83
|
})
|
|
77
84
|
|
|
78
85
|
//Emits
|
|
@@ -104,23 +111,37 @@ const isRecordedInLive = computed(() => {
|
|
|
104
111
|
);
|
|
105
112
|
});
|
|
106
113
|
const statusText = computed(() => {
|
|
107
|
-
if (!props.fetchConference)
|
|
114
|
+
if (!props.fetchConference) {
|
|
115
|
+
return "";
|
|
116
|
+
}
|
|
117
|
+
|
|
108
118
|
switch (props.fetchConference.status) {
|
|
109
119
|
case "PLANNED":
|
|
110
120
|
return t("live in few time");
|
|
121
|
+
|
|
111
122
|
case "PENDING":
|
|
112
|
-
if (props.isAnimatorLive)
|
|
113
|
-
|
|
123
|
+
if (props.isAnimatorLive) {
|
|
124
|
+
return t("Open studio");
|
|
125
|
+
} else {
|
|
126
|
+
return t("live upcoming");
|
|
127
|
+
}
|
|
128
|
+
|
|
114
129
|
case "RECORDING":
|
|
115
130
|
return t("In live");
|
|
131
|
+
|
|
116
132
|
case "DEBRIEFING":
|
|
117
|
-
if ("READY_TO_RECORD" === props.podcast.processingStatus)
|
|
133
|
+
if ("READY_TO_RECORD" === props.podcast.processingStatus) {
|
|
118
134
|
return t("Not recording");
|
|
119
|
-
|
|
135
|
+
} else {
|
|
136
|
+
return t("Debriefing");
|
|
137
|
+
}
|
|
138
|
+
|
|
120
139
|
case "ERROR":
|
|
121
140
|
return t("In error");
|
|
141
|
+
|
|
122
142
|
case "PUBLISHING":
|
|
123
143
|
return t("Publishing");
|
|
144
|
+
|
|
124
145
|
default:
|
|
125
146
|
return "";
|
|
126
147
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
:display-description="0 !== description.length"
|
|
11
11
|
:arrow-direction="arrowDirection"
|
|
12
12
|
:fetch-conference="fetchConference"
|
|
13
|
+
:in-list="inList"
|
|
13
14
|
@hide-description="hideDescription"
|
|
14
15
|
@show-description="showDescription"
|
|
15
16
|
/>
|
|
@@ -50,7 +51,9 @@ import { Conference } from "@/stores/class/conference/conference";
|
|
|
50
51
|
const props = defineProps({
|
|
51
52
|
podcast: { default: () => ({}), type: Object as () => Podcast },
|
|
52
53
|
fetchConference: { default: undefined, type: Object as () => Conference },
|
|
53
|
-
|
|
54
|
+
/** Indicates that the podcast is displayed in a list */
|
|
55
|
+
inList: { default: false, type: Boolean }
|
|
56
|
+
});
|
|
54
57
|
|
|
55
58
|
//Data
|
|
56
59
|
const isMobile = ref(false);
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Component displaying a list of podcast
|
|
3
|
+
DO NOT confuse this with PodcastList from playlist
|
|
4
|
+
-->
|
|
1
5
|
<template>
|
|
2
6
|
<ListPaginate
|
|
3
7
|
id="podcastListPaginate"
|
|
@@ -28,7 +32,11 @@
|
|
|
28
32
|
:key="p.podcastId"
|
|
29
33
|
:min-height="410"
|
|
30
34
|
>
|
|
31
|
-
<PodcastItem
|
|
35
|
+
<PodcastItem
|
|
36
|
+
v-if="0 !== p.podcastId"
|
|
37
|
+
:podcast="p"
|
|
38
|
+
in-list
|
|
39
|
+
/>
|
|
32
40
|
<template #preview>
|
|
33
41
|
<router-link
|
|
34
42
|
:to="{
|
|
@@ -1,9 +1,23 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Component displaying the play button on a podcast.
|
|
3
|
+
If the podcast is not available, an overlay is displayed with a message.
|
|
4
|
+
-->
|
|
1
5
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
|
|
6
|
+
<div
|
|
7
|
+
v-if="!hidePlay || recordingLive"
|
|
8
|
+
:class="{ 'img-blur-background': displayBanner, 'allow-play': classicPodcastPlay }"
|
|
9
|
+
>
|
|
10
|
+
<div
|
|
11
|
+
v-if="displayBanner"
|
|
12
|
+
class="live-image-status bg-dark"
|
|
13
|
+
>
|
|
4
14
|
{{ textVisible }}
|
|
5
15
|
</div>
|
|
6
|
-
|
|
16
|
+
|
|
17
|
+
<div
|
|
18
|
+
class="multi-buttons-play"
|
|
19
|
+
:class="justButtons ? 'play-button-relative' : ''"
|
|
20
|
+
>
|
|
7
21
|
<template v-if="!isLiveToBeRecorded">
|
|
8
22
|
<button
|
|
9
23
|
class="d-flex"
|
|
@@ -16,8 +30,14 @@
|
|
|
16
30
|
v-if="!playingPodcast || (playingPodcast && playerStore.playerVideo)"
|
|
17
31
|
:size="'audio' === hoverType ? 50 : 40"
|
|
18
32
|
/>
|
|
19
|
-
<PodcastIsPlaying v-if="playingPodcast && !playerStore.playerVideo"/>
|
|
20
|
-
<time
|
|
33
|
+
<PodcastIsPlaying v-if="playingPodcast && !playerStore.playerVideo" />
|
|
34
|
+
<time
|
|
35
|
+
v-if="!isVideoPodcast"
|
|
36
|
+
class="ms-1"
|
|
37
|
+
:datetime="durationIso"
|
|
38
|
+
>
|
|
39
|
+
{{ durationString }}
|
|
40
|
+
</time>
|
|
21
41
|
</button>
|
|
22
42
|
<button
|
|
23
43
|
v-if="isVideoPodcast"
|
|
@@ -27,15 +47,35 @@
|
|
|
27
47
|
@mouseenter="hoverType = 'video'"
|
|
28
48
|
@mouseleave="hoverType = ''"
|
|
29
49
|
>
|
|
30
|
-
<PlayVideoIcon
|
|
31
|
-
|
|
32
|
-
|
|
50
|
+
<PlayVideoIcon
|
|
51
|
+
v-if="!playerStore.playerVideo"
|
|
52
|
+
:size="'video' === hoverType ? 50 : 40"
|
|
53
|
+
/>
|
|
54
|
+
<PodcastIsPlaying v-if="playingPodcast && playerStore.playerVideo" />
|
|
55
|
+
<time
|
|
56
|
+
class="ms-2"
|
|
57
|
+
:datetime="durationIso"
|
|
58
|
+
>
|
|
59
|
+
{{ durationString }}
|
|
60
|
+
</time>
|
|
33
61
|
</button>
|
|
34
|
-
|
|
35
|
-
|
|
62
|
+
|
|
63
|
+
<div
|
|
64
|
+
v-if="displayBanner"
|
|
65
|
+
class="special-icon-play-button"
|
|
66
|
+
>
|
|
67
|
+
<component
|
|
68
|
+
:is="iconName"
|
|
69
|
+
:size="16"
|
|
70
|
+
/>
|
|
36
71
|
</div>
|
|
37
72
|
</template>
|
|
38
|
-
<component
|
|
73
|
+
<component
|
|
74
|
+
:is="iconName"
|
|
75
|
+
v-else
|
|
76
|
+
:size="50"
|
|
77
|
+
:title="textVisible"
|
|
78
|
+
/>
|
|
39
79
|
</div>
|
|
40
80
|
</div>
|
|
41
81
|
</template>
|
|
@@ -70,6 +110,8 @@ const props = defineProps({
|
|
|
70
110
|
hidePlay: { default: false, type: Boolean },
|
|
71
111
|
fetchConference: { default: undefined, type: Object as () => Conference },
|
|
72
112
|
justButtons: { default: false, type: Boolean },
|
|
113
|
+
/** Indicates that the podcast is displayed in a list */
|
|
114
|
+
inList: { default: false, type: Boolean }
|
|
73
115
|
})
|
|
74
116
|
|
|
75
117
|
|
|
@@ -119,42 +161,65 @@ const isLiveValidAndVisible = computed(() => {
|
|
|
119
161
|
props.podcast.availability.visibility
|
|
120
162
|
);
|
|
121
163
|
});
|
|
164
|
+
|
|
165
|
+
/** Whether the podcast can be played */
|
|
122
166
|
const classicPodcastPlay = computed(() => {
|
|
123
167
|
return (
|
|
124
168
|
isLiveValidAndVisible.value &&
|
|
125
169
|
!isLiveToBeRecorded.value &&
|
|
126
170
|
("READY_TO_RECORD" === props.podcast.processingStatus ||
|
|
127
171
|
"READY" === props.podcast.processingStatus ||
|
|
128
|
-
|
|
129
|
-
undefined === authStore.authOrgaId))
|
|
172
|
+
"PROCESSING" === props.podcast.processingStatus)
|
|
130
173
|
);
|
|
131
174
|
});
|
|
175
|
+
|
|
176
|
+
const displayBanner = computed(() => {
|
|
177
|
+
return !classicPodcastPlay.value || ("PROCESSING" === props.podcast.processingStatus && !props.inList);
|
|
178
|
+
});
|
|
179
|
+
|
|
132
180
|
const iconName = computed(() => {
|
|
133
|
-
if (isLiveToBeRecorded.value)
|
|
181
|
+
if (isLiveToBeRecorded.value) {
|
|
182
|
+
return ClockOutlineIcon;
|
|
183
|
+
}
|
|
184
|
+
|
|
134
185
|
if ("READY" === props.podcast.processingStatus || props.fetchConference) {
|
|
135
|
-
if (!props.podcast.valid)
|
|
186
|
+
if (!props.podcast.valid) {
|
|
187
|
+
return CheckIcon;
|
|
188
|
+
}
|
|
189
|
+
|
|
136
190
|
if (
|
|
137
191
|
!props.podcast.availability.visibility &&
|
|
138
192
|
props.podcast.availability.date
|
|
139
|
-
)
|
|
193
|
+
) {
|
|
140
194
|
return ClockOutlineIcon;
|
|
195
|
+
}
|
|
196
|
+
|
|
141
197
|
return EyeOffOutlineIcon;
|
|
142
198
|
}
|
|
199
|
+
|
|
143
200
|
if (
|
|
144
201
|
"PLANNED" === props.podcast.processingStatus ||
|
|
145
202
|
"PROCESSING" === props.podcast.processingStatus
|
|
146
|
-
)
|
|
203
|
+
) {
|
|
147
204
|
return TimerSandEmptyIcon;
|
|
148
|
-
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if ("CANCELED" === props.podcast.processingStatus) {
|
|
208
|
+
return CancelIcon;
|
|
209
|
+
}
|
|
149
210
|
return AlertIcon;
|
|
150
211
|
});
|
|
212
|
+
|
|
213
|
+
/** The text to display when the podcast is not ready */
|
|
151
214
|
const textVisible = computed(() => {
|
|
152
215
|
if (isLiveToBeRecorded.value){
|
|
153
216
|
return t("Podcast linked to waiting live");
|
|
154
217
|
}
|
|
155
218
|
|
|
156
219
|
if ("READY" === props.podcast.processingStatus || props.fetchConference) {
|
|
157
|
-
if (!props.podcast.valid)
|
|
220
|
+
if (!props.podcast.valid) {
|
|
221
|
+
return t("Podcast to validate");
|
|
222
|
+
}
|
|
158
223
|
if (
|
|
159
224
|
!props.podcast.availability.visibility &&
|
|
160
225
|
props.podcast.availability.date
|
|
@@ -174,6 +239,7 @@ const textVisible = computed(() => {
|
|
|
174
239
|
}
|
|
175
240
|
return t("Podcast in error");
|
|
176
241
|
});
|
|
242
|
+
|
|
177
243
|
const recordingLive = computed(() => {
|
|
178
244
|
return (
|
|
179
245
|
undefined !== props.fetchConference &&
|
|
@@ -188,7 +254,9 @@ const durationString = computed(() => {
|
|
|
188
254
|
);
|
|
189
255
|
});
|
|
190
256
|
const durationIso = computed(() => {
|
|
191
|
-
if (!props.podcast || props.podcast.duration <= 1)
|
|
257
|
+
if (!props.podcast || props.podcast.duration <= 1) {
|
|
258
|
+
return "";
|
|
259
|
+
}
|
|
192
260
|
return dayjs.duration({ milliseconds: props.podcast.duration }).toISOString();
|
|
193
261
|
});
|
|
194
262
|
|
|
@@ -230,6 +298,10 @@ function play(isVideo: boolean): void {
|
|
|
230
298
|
background-color:var(--octopus-background-transparent);
|
|
231
299
|
// Allow pointer events to go through (allow click on image beneath blur)
|
|
232
300
|
pointer-events: none;
|
|
301
|
+
|
|
302
|
+
&.allow-play {
|
|
303
|
+
pointer-events: all;
|
|
304
|
+
}
|
|
233
305
|
}
|
|
234
306
|
|
|
235
307
|
.live-image-status {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
:tabindex="isSwitch ? '-1' : '0'"
|
|
12
12
|
@input="emit('update:textInit', !textInit)"
|
|
13
13
|
@click="emitClickAction"
|
|
14
|
-
|
|
14
|
+
>
|
|
15
15
|
<button
|
|
16
16
|
v-if="isSwitch"
|
|
17
17
|
class="slider btn-transparent"
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
<label
|
|
25
25
|
class="c-hand"
|
|
26
26
|
:class="[classLabel, displayLabel ? '' : 'd-none', isDisabled ? 'disabled' : '']"
|
|
27
|
-
|
|
27
|
+
@click="clickSlider"
|
|
28
28
|
>
|
|
29
29
|
{{ label }}
|
|
30
30
|
</label>
|
|
@@ -4,15 +4,21 @@
|
|
|
4
4
|
:class="{ 'form-margin': displayLabel }"
|
|
5
5
|
>
|
|
6
6
|
<div class="d-flex align-items-center">
|
|
7
|
-
<slot name="complementLabel"/>
|
|
7
|
+
<slot name="complementLabel" />
|
|
8
8
|
<component
|
|
9
9
|
:is="isWysiwyg? 'div': 'label'"
|
|
10
10
|
:class="[classLabel, displayLabel ? '' : 'd-none']"
|
|
11
11
|
:for="isWysiwyg ? '': computedInputId"
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
>
|
|
13
|
+
{{ label }}
|
|
14
|
+
<AsteriskIcon
|
|
15
|
+
v-if="displayRequired"
|
|
16
|
+
:size="10"
|
|
17
|
+
class="ms-1 mb-2"
|
|
18
|
+
:title="t('Mandatory input')"
|
|
19
|
+
/>
|
|
14
20
|
</component>
|
|
15
|
-
<slot name="afterTitle"/>
|
|
21
|
+
<slot name="afterTitle" />
|
|
16
22
|
<template v-if="popover">
|
|
17
23
|
<button
|
|
18
24
|
:id="'popover' + computedInputId"
|
|
@@ -32,9 +38,9 @@
|
|
|
32
38
|
<!-- eslint-enable -->
|
|
33
39
|
</ClassicPopover>
|
|
34
40
|
</template>
|
|
35
|
-
<slot name="afterHelp"/>
|
|
41
|
+
<slot name="afterHelp" />
|
|
36
42
|
</div>
|
|
37
|
-
<slot name="betweenTitleInput"/>
|
|
43
|
+
<slot name="betweenTitleInput" />
|
|
38
44
|
<input
|
|
39
45
|
v-if="!isWysiwyg && !isTextarea"
|
|
40
46
|
v-show="showField"
|
|
@@ -55,7 +61,7 @@
|
|
|
55
61
|
:disabled="isDisable"
|
|
56
62
|
:required="!canBeNull"
|
|
57
63
|
:autocomplete="autocompleteType"
|
|
58
|
-
|
|
64
|
+
>
|
|
59
65
|
<textarea
|
|
60
66
|
v-else-if="isTextarea"
|
|
61
67
|
v-show="showField"
|
|
@@ -89,10 +95,16 @@
|
|
|
89
95
|
:is-top-position="true"
|
|
90
96
|
@emoji-selected="addEmojiSelected"
|
|
91
97
|
/>
|
|
92
|
-
<div
|
|
98
|
+
<div
|
|
99
|
+
v-if="isWysiwyg"
|
|
100
|
+
class="h6"
|
|
101
|
+
>
|
|
93
102
|
{{ t("Characters number calculated over HTML code") }}
|
|
94
103
|
</div>
|
|
95
|
-
<div
|
|
104
|
+
<div
|
|
105
|
+
v-else-if="'' !== indicText"
|
|
106
|
+
class="text-indic"
|
|
107
|
+
>
|
|
96
108
|
{{ indicText }}
|
|
97
109
|
</div>
|
|
98
110
|
<div
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="classic-select" :class="{ 'form-margin': displayLabel }">
|
|
3
|
-
<label v-show="displayLabel" :for="idSelect" :class="classLabel">
|
|
4
|
-
label
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
<label v-show="displayLabel" :for="idSelect" :class="classLabel">
|
|
4
|
+
{{ label }}
|
|
5
|
+
<AsteriskIcon v-if="displayRequired" :size="10" class="ms-1 mb-2" :title="t('Mandatory input')"/>
|
|
6
|
+
<slot name="after-label" />
|
|
7
|
+
</label>
|
|
8
|
+
|
|
7
9
|
<select
|
|
8
10
|
:id="idSelect"
|
|
9
11
|
:value="textInit"
|
|
@@ -28,37 +30,36 @@
|
|
|
28
30
|
</select>
|
|
29
31
|
</div>
|
|
30
32
|
</template>
|
|
33
|
+
|
|
31
34
|
<script setup lang="ts">
|
|
32
35
|
import AsteriskIcon from "vue-material-design-icons/Asterisk.vue";
|
|
33
36
|
import { computed } from "vue";
|
|
34
37
|
import { useI18n } from "vue-i18n";
|
|
35
38
|
|
|
39
|
+
export interface SelectOption<T = number|string|undefined> {
|
|
40
|
+
title: string;
|
|
41
|
+
value: T;
|
|
42
|
+
fontFamily?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
//Props
|
|
37
46
|
const props = defineProps({
|
|
38
47
|
idSelect: { default: "", type: String },
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
fontFamily?: string;
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
textInit: { default: undefined, type: [String, Number] },
|
|
58
|
-
classLabel: { default: "form-label", type: String },
|
|
59
|
-
orderOptions: { default: true, type: Boolean},
|
|
60
|
-
placeholder: { default: undefined, type: String},
|
|
61
|
-
displayRequired: { default: false, type: Boolean },
|
|
48
|
+
label: { default: "", type: String },
|
|
49
|
+
displayLabel: { default: true, type: Boolean },
|
|
50
|
+
transparent: { default: false, type: Boolean },
|
|
51
|
+
isDisabled: { default: false, type: Boolean },
|
|
52
|
+
options: {
|
|
53
|
+
default: () => [],
|
|
54
|
+
type: Array as () => Array<SelectOption>,
|
|
55
|
+
},
|
|
56
|
+
topOption: { default: undefined,type: Object as () => SelectOption,
|
|
57
|
+
},
|
|
58
|
+
textInit: { default: undefined, type: [String, Number] },
|
|
59
|
+
classLabel: { default: "form-label", type: String },
|
|
60
|
+
orderOptions: { default: true, type: Boolean},
|
|
61
|
+
placeholder: { default: undefined, type: String},
|
|
62
|
+
displayRequired: { default: false, type: Boolean },
|
|
62
63
|
})
|
|
63
64
|
|
|
64
65
|
|
|
@@ -100,9 +101,8 @@ function onChange(value:string){
|
|
|
100
101
|
emit('update:textInit', value)
|
|
101
102
|
}
|
|
102
103
|
</script>
|
|
103
|
-
<style lang="scss">
|
|
104
|
-
|
|
105
104
|
|
|
105
|
+
<style lang="scss">
|
|
106
106
|
.octopus-app {
|
|
107
107
|
select option:is(:checked, :hover){
|
|
108
108
|
box-shadow: 0 0 10px 100px var(--octopus-secondary) inset;
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
:src="imageUrl"
|
|
22
22
|
aria-hidden="true"
|
|
23
23
|
alt=""
|
|
24
|
-
|
|
24
|
+
>
|
|
25
25
|
<span>{{ title }}</span>
|
|
26
26
|
<slot name="afterTitle"/>
|
|
27
27
|
<ChevronDownIcon class="ms-auto" :class="{ 'arrow-transform': isOpen }" />
|
|
@@ -58,7 +58,7 @@ const emit = defineEmits(["open"]);
|
|
|
58
58
|
const isOpen = ref(false);
|
|
59
59
|
|
|
60
60
|
//Watch
|
|
61
|
-
watch(isOpen, () => emit("open"));
|
|
61
|
+
watch(isOpen, () => emit("open", isOpen.value));
|
|
62
62
|
|
|
63
63
|
onMounted(()=>{
|
|
64
64
|
isOpen.value = props.initOpen;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Simple component to display a message to the user.
|
|
3
|
+
Usage:
|
|
4
|
+
<ClassicAlert type='info'>This is an information</ClassicAlert>
|
|
5
|
+
<ClassicAlert type='success'>Your changes have been saved</ClassicAlert>
|
|
6
|
+
<ClassicAlert type='warning'>Some data will be lost</ClassicAlert>
|
|
7
|
+
<ClassicAlert type='error'>An error occured while saving</ClassicAlert>
|
|
8
|
+
-->
|
|
9
|
+
<template>
|
|
10
|
+
<div
|
|
11
|
+
class="p-2 pe-4 my-2 rounded d-flex alert"
|
|
12
|
+
:class="cardClass"
|
|
13
|
+
>
|
|
14
|
+
<!-- The icon -->
|
|
15
|
+
<component
|
|
16
|
+
:is="iconComponent"
|
|
17
|
+
v-if="!noIcon"
|
|
18
|
+
class="icon"
|
|
19
|
+
:size="30"
|
|
20
|
+
/>
|
|
21
|
+
|
|
22
|
+
<!-- Main content -->
|
|
23
|
+
<span class="ms-2">
|
|
24
|
+
<strong v-if="title">
|
|
25
|
+
{{ title }}
|
|
26
|
+
<br>
|
|
27
|
+
</strong>
|
|
28
|
+
|
|
29
|
+
<slot />
|
|
30
|
+
</span>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<script setup lang="ts">
|
|
35
|
+
import { computed } from 'vue';
|
|
36
|
+
|
|
37
|
+
import Alert from 'vue-material-design-icons/Alert.vue';
|
|
38
|
+
import CheckCircle from 'vue-material-design-icons/CheckCircle.vue';
|
|
39
|
+
import CloseCircle from 'vue-material-design-icons/CloseCircle.vue';
|
|
40
|
+
import Information from 'vue-material-design-icons/Information.vue';
|
|
41
|
+
|
|
42
|
+
const { type } = defineProps<{
|
|
43
|
+
/** Disables the icon when true */
|
|
44
|
+
noIcon?: boolean;
|
|
45
|
+
/** An optional title for the alert */
|
|
46
|
+
title?: string;
|
|
47
|
+
/** The type of message */
|
|
48
|
+
type: 'info'|'success'|'warning'|'error';
|
|
49
|
+
}>();
|
|
50
|
+
|
|
51
|
+
/** The class applied to the alert */
|
|
52
|
+
const cardClass = computed(() => {
|
|
53
|
+
return 'alert-' + type;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const iconComponent = computed(() => {
|
|
57
|
+
if (type === 'info') {
|
|
58
|
+
return Information;
|
|
59
|
+
} else if (type === 'success') {
|
|
60
|
+
return CheckCircle;
|
|
61
|
+
} else if (type === 'warning') {
|
|
62
|
+
return Alert;
|
|
63
|
+
} else if (type === 'error') {
|
|
64
|
+
return CloseCircle;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<style lang="scss" scoped>
|
|
70
|
+
.alert {
|
|
71
|
+
//color: white;
|
|
72
|
+
border: 1px solid;
|
|
73
|
+
border-left: 8px solid;
|
|
74
|
+
|
|
75
|
+
.icon {
|
|
76
|
+
align-items: start !important;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
$types: (
|
|
80
|
+
'success': hsl(from var(--octopus-primary) h s 45),
|
|
81
|
+
'info': var(--octopus-primary),
|
|
82
|
+
'warning': var(--octopus-warning),
|
|
83
|
+
'error': var(--octopus-danger),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
&.alert {
|
|
87
|
+
@each $type, $color in $types {
|
|
88
|
+
&-#{$type} {
|
|
89
|
+
background-color: hsl(from #{$color} h s 95) !important;
|
|
90
|
+
border-color: #{$color} !important;
|
|
91
|
+
.icon {
|
|
92
|
+
color: #{$color};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
</style>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
A simple component to display a help button that shows its message when
|
|
3
|
+
hovered.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
<ClassicHelpButton>This is my help message</ClassicHelpButton>
|
|
7
|
+
-->
|
|
8
|
+
<template>
|
|
9
|
+
<button
|
|
10
|
+
:id="computedId"
|
|
11
|
+
class="btn-transparent"
|
|
12
|
+
>
|
|
13
|
+
<!-- Button icon -->
|
|
14
|
+
<HelpCircleIcon :size="30" />
|
|
15
|
+
|
|
16
|
+
<!-- Tooltip -->
|
|
17
|
+
<ClassicPopover
|
|
18
|
+
:target="computedId"
|
|
19
|
+
popover-class="help-popover"
|
|
20
|
+
>
|
|
21
|
+
<div class="content">
|
|
22
|
+
<slot />
|
|
23
|
+
</div>
|
|
24
|
+
</ClassicPopover>
|
|
25
|
+
</button>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script setup lang="ts">
|
|
29
|
+
import { computed, getCurrentInstance } from 'vue';
|
|
30
|
+
|
|
31
|
+
import HelpCircleIcon from "vue-material-design-icons/HelpCircle.vue";
|
|
32
|
+
import ClassicPopover from './ClassicPopover.vue';
|
|
33
|
+
|
|
34
|
+
/** The internal ID for the elements */
|
|
35
|
+
const computedId = computed(() => {
|
|
36
|
+
return 'popover-' + getCurrentInstance()?.uid;
|
|
37
|
+
});
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<style lang="scss" scoped>
|
|
41
|
+
.content {
|
|
42
|
+
font: revert;
|
|
43
|
+
font-size: 18px !important;
|
|
44
|
+
text-align: start;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.help-popover {
|
|
48
|
+
background-color: var(--octopus-secondary);
|
|
49
|
+
}
|
|
50
|
+
</style>
|
|
@@ -163,14 +163,13 @@ function changeLanguage(): void {
|
|
|
163
163
|
* @param organisation The new organisation to focus on, or undefined to remove focus
|
|
164
164
|
*/
|
|
165
165
|
async function onOrganisationSelected( organisation: Organisation | undefined): Promise<void> {
|
|
166
|
-
// TODO use router utils
|
|
167
166
|
if (organisation?.id) {
|
|
168
167
|
router.push({
|
|
169
|
-
query: { ...route.query, ...{ productor: organisation.id, o:undefined,
|
|
168
|
+
query: { ...route.query, ...{ productor: organisation.id, o:undefined, viewall: "false" } },
|
|
170
169
|
});
|
|
171
170
|
}else{
|
|
172
171
|
router.push({
|
|
173
|
-
query: { ...route.query, ...{ productor: undefined,
|
|
172
|
+
query: { ...route.query, ...{ productor: undefined, viewall: "true" } },
|
|
174
173
|
});
|
|
175
174
|
}
|
|
176
175
|
}
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
:organisation-id="authOrgaId"
|
|
28
28
|
/>
|
|
29
29
|
<CommentSection :podcast="podcast" />
|
|
30
|
+
|
|
31
|
+
<!-- Suggestions -->
|
|
30
32
|
<PodcastInlineList
|
|
31
33
|
:emission-id="podcast.emission.emissionId"
|
|
32
34
|
:href="'/main/pub/emission/' + podcast.emission.emissionId"
|
|
@@ -39,6 +41,7 @@
|
|
|
39
41
|
<PodcastInlineList
|
|
40
42
|
class="mt-4"
|
|
41
43
|
title-tag="h3"
|
|
44
|
+
:organisation-id="[podcast.organisation.id]"
|
|
42
45
|
:podcast-id="podcastId"
|
|
43
46
|
:title="t('Suggested listening')"
|
|
44
47
|
/>
|
|
@@ -51,6 +54,7 @@
|
|
|
51
54
|
:href="'/main/pub/category/' + c.id"
|
|
52
55
|
:title="t('More episodes of this category : ', { name: c.name })"
|
|
53
56
|
:button-text="t('All podcast button', { name: c.name })"
|
|
57
|
+
:organisation-id="[podcast.organisation.id]"
|
|
54
58
|
/>
|
|
55
59
|
</ClassicLazy>
|
|
56
60
|
</section>
|
package/src/router/utils.ts
CHANGED
|
@@ -91,11 +91,11 @@ export function setupRouter(router: Router, getMyOrgaActive: (authStore: AuthSto
|
|
|
91
91
|
newQuery.productor = filterStore.filterOrgaId;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
// Enable '
|
|
95
|
-
if ((from.query.
|
|
96
|
-
newQuery.
|
|
94
|
+
// Enable 'viewall' mode if already active
|
|
95
|
+
if ((from.query.viewall === "true" || to.query.viewall === "true") && to.query.viewall !== "false") {
|
|
96
|
+
newQuery.viewall = "true";
|
|
97
97
|
} else {
|
|
98
|
-
delete newQuery.
|
|
98
|
+
delete newQuery.viewall;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
// If the queries are different, update path
|
package/src/stores/AuthStore.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { KeycloakInfo } from "@/stores/class/user/person";
|
|
|
6
6
|
import { VideoConfig } from "@/stores/class/config/videoConfig";
|
|
7
7
|
import classicApi from "../api/classicApi";
|
|
8
8
|
|
|
9
|
-
interface AuthParam{
|
|
9
|
+
interface AuthParam {
|
|
10
10
|
accessToken?: string;
|
|
11
11
|
refreshToken?: string;
|
|
12
12
|
expiration?: Date|string;
|
|
@@ -45,7 +45,7 @@ export const useAuthStore = defineStore("AuthStore", {
|
|
|
45
45
|
comments: undefined,
|
|
46
46
|
attributes: {
|
|
47
47
|
RSS_CONTACT: undefined,
|
|
48
|
-
}
|
|
48
|
+
}
|
|
49
49
|
},
|
|
50
50
|
authVideoConfig: { active: false },
|
|
51
51
|
}),
|
|
@@ -32,7 +32,7 @@ export const useFilterStore = defineStore("FilterStore", () => {
|
|
|
32
32
|
* ID of the current organisation.
|
|
33
33
|
*/
|
|
34
34
|
const filterOrgaId = computed((): string|undefined => {
|
|
35
|
-
if (route?.query.
|
|
35
|
+
if (route?.query.viewall === "true") {
|
|
36
36
|
return undefined;
|
|
37
37
|
} else if (route?.query.productor) {
|
|
38
38
|
return route.query.productor as string;
|
|
@@ -46,7 +46,7 @@ export const useFilterStore = defineStore("FilterStore", () => {
|
|
|
46
46
|
/**
|
|
47
47
|
* The ID of the current organisation, regardless of other options.
|
|
48
48
|
* Use this if you want to know the organisation of the user even in
|
|
49
|
-
* unfocused mode (ie
|
|
49
|
+
* unfocused mode (ie viewall = true)
|
|
50
50
|
*/
|
|
51
51
|
const realOrgaId = computed(() => {
|
|
52
52
|
return _filterOrgaId.value ?? undefined;
|
|
@@ -22,13 +22,13 @@ export function emptyOrganisationData(): Organisation {
|
|
|
22
22
|
return {
|
|
23
23
|
imageUrl: "",
|
|
24
24
|
id: "",
|
|
25
|
-
name: ""
|
|
25
|
+
name: ""
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
export function emptyOrgaData(defaultName: string): Organisation {
|
|
29
29
|
return {
|
|
30
30
|
imageUrl: "/img/emptypodcast.webp",
|
|
31
31
|
id: "",
|
|
32
|
-
name: defaultName
|
|
32
|
+
name: defaultName
|
|
33
33
|
};
|
|
34
34
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module 'sockjs-client/dist/sockjs';
|