From d3e6af6f82b7ef9a6e536ffa5095bff75243088b Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Fri, 12 Dec 2025 19:13:58 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=B8(frontend)=20rework=20the=20meeting?= =?UTF-8?q?=20tools=20side=20panel=20UX?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explicitly explain that transcription is reserved for public servants. Remove the temporary beta form: the feature is now available to all public servants, with restrictions based on domain. Make white-labeling rules explicit and clarify who to contact for access. The beta form created frustration, with users registering and never hearing back from the team. Improve guidance when a user may be the meeting host but is not logged in, and therefore cannot activate recording. Add a clear hint and a quick action to log in. This decision is based on frequent support requests where users could not understand why recording was unavailable while they were simply not logged in. --- docs/installation/kubernetes.md | 1 - src/backend/meet/settings.py | 3 - src/frontend/src/api/useConfig.ts | 3 - .../features/home/components/IntroSlider.tsx | 30 +- .../components/ScreenRecordingSidePanel.tsx | 102 ++++ .../components/TranscriptSidePanel.tsx | 483 +++++++++++------- .../rooms/livekit/components/Tools.tsx | 9 +- src/frontend/src/locales/de/rooms.json | 25 +- src/frontend/src/locales/en/rooms.json | 23 +- src/frontend/src/locales/fr/rooms.json | 23 +- src/frontend/src/locales/nl/rooms.json | 23 +- .../env.d/dev-dinum/values.meet.yaml.gotmpl | 1 - .../dev-keycloak/values.meet.yaml.gotmpl | 1 - 13 files changed, 476 insertions(+), 251 deletions(-) diff --git a/docs/installation/kubernetes.md b/docs/installation/kubernetes.md index b5c3f578..62f4aeb8 100644 --- a/docs/installation/kubernetes.md +++ b/docs/installation/kubernetes.md @@ -277,7 +277,6 @@ These are the environmental options available on meet backend. | FRONTEND_CUSTOM_CSS_URL | URL of an additional CSS file to load in the frontend app. If set, a `` tag with this URL as href is added to the `` of the frontend app | | | FRONTEND_ANALYTICS | Analytics information | {} | | FRONTEND_SUPPORT | Crisp frontend support configuration, also you can pass help articles, with `help_article_transcript`, `help_article_recording`, `help_article_more_tools` | {} | -| FRONTEND_TRANSCRIPT | Frontend transcription configuration, you can pass a beta form, with `form_beta_users` | {} | | FRONTEND_MANIFEST_LINK | Link to the "Learn more" button on the homepage | {} | | FRONTEND_SILENCE_LIVEKIT_DEBUG | Silence LiveKit debug logs | false | | FRONTEND_IS_SILENT_LOGIN_ENABLED | Enable silent login feature | true | diff --git a/src/backend/meet/settings.py b/src/backend/meet/settings.py index c680f02d..5f7f6c1b 100755 --- a/src/backend/meet/settings.py +++ b/src/backend/meet/settings.py @@ -342,9 +342,6 @@ class Base(Configuration): "use_proconnect_button": values.BooleanValue( False, environ_name="FRONTEND_USE_PROCONNECT_BUTTON", environ_prefix=None ), - "transcript": values.DictValue( - {}, environ_name="FRONTEND_TRANSCRIPT", environ_prefix=None - ), "manifest_link": values.Value( None, environ_name="FRONTEND_MANIFEST_LINK", environ_prefix=None ), diff --git a/src/frontend/src/api/useConfig.ts b/src/frontend/src/api/useConfig.ts index 30ea268e..5c760092 100644 --- a/src/frontend/src/api/useConfig.ts +++ b/src/frontend/src/api/useConfig.ts @@ -17,9 +17,6 @@ export interface ApiConfig { feedback: { url: string } - transcript: { - form_beta_users: string - } silence_livekit_debug_logs?: boolean is_silent_login_enabled?: boolean custom_css_url?: string diff --git a/src/frontend/src/features/home/components/IntroSlider.tsx b/src/frontend/src/features/home/components/IntroSlider.tsx index f9c5efca..10cf7e81 100644 --- a/src/frontend/src/features/home/components/IntroSlider.tsx +++ b/src/frontend/src/features/home/components/IntroSlider.tsx @@ -1,10 +1,9 @@ import { styled } from '@/styled-system/jsx' import { css } from '@/styled-system/css' -import { Button, LinkButton } from '@/primitives' +import { Button } from '@/primitives' import { RiArrowLeftSLine, RiArrowRightSLine } from '@remixicon/react' -import { useMemo, useState } from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' -import { useConfig } from '@/api/useConfig' const Heading = styled('h2', { base: { @@ -165,14 +164,7 @@ export const IntroSlider = () => { const [slideIndex, setSlideIndex] = useState(0) const { t } = useTranslation('home', { keyPrefix: 'introSlider' }) - const { data } = useConfig() - - const filteredSlides = useMemo( - () => (data?.transcript?.form_beta_users ? SLIDES : SLIDES.slice(0, 2)), - [data] - ) - - const NUMBER_SLIDES = filteredSlides.length + const NUMBER_SLIDES = SLIDES.length return ( @@ -198,24 +190,12 @@ export const IntroSlider = () => { - {filteredSlides.map((slide, index) => ( + {SLIDES.map((slide, index) => ( {t(`${slide.key}.imgAlt`)} {t(`${slide.key}.title`)} {t(`${slide.key}.body`)} - {slide.isAvailableInBeta && ( - - {t('beta.text')} - - )} ))} @@ -241,7 +221,7 @@ export const IntroSlider = () => { display: { base: 'none', xsm: 'block' }, })} > - {filteredSlides.map((_, index) => ( + {SLIDES.map((_, index) => ( ))} diff --git a/src/frontend/src/features/recording/components/ScreenRecordingSidePanel.tsx b/src/frontend/src/features/recording/components/ScreenRecordingSidePanel.tsx index 71d830b7..7b577093 100644 --- a/src/frontend/src/features/recording/components/ScreenRecordingSidePanel.tsx +++ b/src/frontend/src/features/recording/components/ScreenRecordingSidePanel.tsx @@ -5,6 +5,7 @@ import { useRoomId } from '@/features/rooms/livekit/hooks/useRoomId' import { useRoomContext } from '@livekit/components-react' import { RecordingMode, + useHasFeatureWithoutAdminRights, useIsRecordingTransitioning, useStartRecording, useStopRecording, @@ -25,6 +26,9 @@ import { Spinner } from '@/primitives/Spinner' import { useConfig } from '@/api/useConfig' import humanizeDuration from 'humanize-duration' import i18n from 'i18next' +import { FeatureFlags } from '@/features/analytics/enums' +import { LoginButton } from '@/components/LoginButton' +import { useUser } from '@/features/auth' export const ScreenRecordingSidePanel = () => { const { data } = useConfig() @@ -34,6 +38,13 @@ export const ScreenRecordingSidePanel = () => { const [isErrorDialogOpen, setIsErrorDialogOpen] = useState('') + const hasFeatureWithoutAdminRights = useHasFeatureWithoutAdminRights( + RecordingMode.ScreenRecording, + FeatureFlags.ScreenRecording + ) + + const { isLoggedIn } = useUser() + const { notifyParticipants } = useNotifyParticipants() const roomId = useRoomId() @@ -117,6 +128,97 @@ export const ScreenRecordingSidePanel = () => { [isLoading, isRecordingTransitioning, statuses, isRoomConnected] ) + if (hasFeatureWithoutAdminRights) { + return ( +
+ {''} + {t('notAdminOrOwner.heading')} + + {t('notAdminOrOwner.body')} +
+ {data?.support?.help_article_recording && ( + + {t('notAdminOrOwner.linkMore')} + + )} +
+ {!isLoggedIn && ( +
+ + {t('notAdminOrOwner.login.heading')} + + + {t('notAdminOrOwner.login.body')} + +
+ +
+
+ )} +
+ ) + } + return (
{ const { data } = useConfig() + const { isLoggedIn } = useUser() + const [isLoading, setIsLoading] = useState(false) const { t } = useTranslation('rooms', { keyPrefix: 'transcript' }) @@ -127,6 +131,184 @@ export const TranscriptSidePanel = () => { [isLoading, isRecordingTransitioning, statuses, isRoomConnected] ) + if (hasFeatureWithoutAdminRights) { + return ( +
+ {''} + {t('notAdminOrOwner.heading')} + + {t('notAdminOrOwner.body')} +
+ {data?.support?.help_article_transcript && ( + + {t('notAdminOrOwner.linkMore')} + + )} +
+ {!isLoggedIn && ( +
+ + {t('notAdminOrOwner.login.heading')} + + + {t('notAdminOrOwner.login.body')} + +
+ +
+
+ )} +
+ ) + } + + if (!hasTranscriptAccess) { + return ( +
+ {''} + {t('premium.heading')} + + {t('premium.body')}{' '} + {data?.support?.help_article_transcript && ( + + {t('linkMore')} + + )} + + {!isLoggedIn && ( +
+ + {t('premium.login.heading')} + + {t('premium.login.body')} +
+ +
+
+ )} +
+ ) + } + return (
{ }, })} /> - {!hasTranscriptAccess ? ( - <> - {hasFeatureWithoutAdminRights ? ( - <> - {t('notAdminOrOwner.heading')} - - {t('notAdminOrOwner.body')} -
- {data?.support?.help_article_transcript && ( - - {t('notAdminOrOwner.linkMore')} - - )} -
- - ) : ( - <> - {t('beta.heading')} - - {t('beta.body')}{' '} - {data?.support?.help_article_transcript && ( - - {t('start.linkMore')} - - )} - - {data?.transcript.form_beta_users && ( - + {statuses.isStarted ? ( + <> + + {t('stop.heading')} + + + {t('stop.body')} + + + + ) : ( + <> + {statuses.isStopping || isPendingToStop ? ( + <> + + {t('stopping.heading')} + + + {t('stopping.body')} + + + + ) : ( + <> + + {t('start.heading')} + + + {t('start.body', { + duration_message: data?.recording?.max_duration + ? t('durationMessage', { + max_duration: humanizeDuration( + data?.recording?.max_duration, + { + language: i18n.language, + } + ), + }) + : '', + })}{' '} + {data?.support?.help_article_transcript && ( + + {t('start.linkMore')} + + )} + + - - ) : ( - <> - {statuses.isStopping || isPendingToStop ? ( - <> - - {t('stopping.heading')} - - - {t('stopping.body')} - - - - ) : ( - <> - - {t('start.heading')} - - - {t('start.body', { - duration_message: data?.recording?.max_duration - ? t('durationMessage', { - max_duration: humanizeDuration( - data?.recording?.max_duration, - { - language: i18n.language, - } - ), - }) - : '', - })}{' '} - {data?.support?.help_article_transcript && ( - - {t('start.linkMore')} - - )} - - - - )} - - )} - - )} + {statuses.isStarting || isPendingToStart ? ( + <> + + {t('start.loading')} + + ) : ( + t('start.button') + )} + + + )} + + )} + { RecordingMode.Transcript ) - const hasScreenRecordingAccess = useHasRecordingAccess( - RecordingMode.ScreenRecording, - FeatureFlags.ScreenRecording + const isScreenRecordingEnabled = useIsRecordingModeEnabled( + RecordingMode.ScreenRecording ) switch (activeSubPanelId) { @@ -155,7 +152,7 @@ export const Tools = () => { onPress={() => openTranscript()} /> )} - {hasScreenRecordingAccess && ( + {isScreenRecordingEnabled && ( mode_standby} title={t('tools.screenRecording.title')} diff --git a/src/frontend/src/locales/de/rooms.json b/src/frontend/src/locales/de/rooms.json index 6df3345e..1bd3dfa4 100644 --- a/src/frontend/src/locales/de/rooms.json +++ b/src/frontend/src/locales/de/rooms.json @@ -320,7 +320,11 @@ "notAdminOrOwner": { "heading": "Zugriff eingeschränkt", "body": "Aus Sicherheitsgründen kann nur der Ersteller oder ein Administrator des Meetings eine Transkription (Beta) starten.", - "linkMore": "Mehr erfahren" + "linkMore": "Mehr erfahren", + "login": { + "heading": "Anmeldung erforderlich", + "body": "Nur der Ersteller des Meetings oder ein Administrator kann die Transkription starten. Melden Sie sich an, um Ihre Berechtigungen zu überprüfen." + } }, "stop": { "heading": "Transkription läuft...", @@ -331,10 +335,13 @@ "heading": "Daten werden gespeichert…", "body": "Sie können das Meeting verlassen, wenn Sie möchten; die Aufzeichnung wird automatisch beendet." }, - "beta": { - "heading": "Werde Beta-Tester", - "body": "Zeichne dein Meeting auf. Du erhältst eine Zusammenfassung per E-Mail nach dem Meeting.", - "button": "Anmelden" + "premium": { + "heading": "Premium-Funktion", + "body": "Diese Funktion ist öffentlichen Bediensteten vorbehalten. Wenn Ihre E-Mail-Adresse nicht autorisiert ist, kontaktieren Sie bitte den Support, um Zugriff zu erhalten.", + "login": { + "heading": "Anmeldung erforderlich", + "body": "Nur der Ersteller des Meetings oder ein Administrator kann die Transkription starten. Melden Sie sich an, um Ihre Berechtigungen zu überprüfen." + } }, "alert": { "title": "Transkription fehlgeschlagen", @@ -356,8 +363,12 @@ }, "notAdminOrOwner": { "heading": "Zugriff eingeschränkt", - "body": "Aus Sicherheitsgründen kann nur der Ersteller oder ein Administrator des Meetings eine videoaufnahme (Beta) starten.", - "linkMore": "Mehr erfahren" + "body": "Aus Sicherheitsgründen kann nur der Ersteller oder ein Administrator des Meetings eine Videoaufnahme (Beta) starten.", + "linkMore": "Mehr erfahren", + "login": { + "heading": "Anmeldung erforderlich", + "body": "Nur der Ersteller der Besprechung oder ein Administrator kann die Aufzeichnung starten. Melden Sie sich an, um Ihre Berechtigungen zu überprüfen." + } }, "stopping": { "heading": "Daten werden gespeichert…", diff --git a/src/frontend/src/locales/en/rooms.json b/src/frontend/src/locales/en/rooms.json index 7b51a497..b7b50cf5 100644 --- a/src/frontend/src/locales/en/rooms.json +++ b/src/frontend/src/locales/en/rooms.json @@ -320,7 +320,11 @@ "notAdminOrOwner": { "heading": "Restricted Access", "body": "For security reasons, only the meeting creator or an admin can start a transcription (beta).", - "linkMore": "Learn more" + "linkMore": "Learn more", + "login": { + "heading": "Login Required", + "body": "Only the meeting creator or an admin can start a transcription. Log in to verify your permissions." + } }, "stop": { "heading": "Transcription in progress...", @@ -331,10 +335,13 @@ "heading": "Saving your data…", "body": "You can leave the meeting if you wish; the recording will finish automatically." }, - "beta": { - "heading": "Become a beta tester", - "body": "Record your meeting for later. You will receive a summary by email once the meeting is finished.", - "button": "Sign up" + "premium": { + "heading": "Premium feature", + "body": "This feature is reserved for public agents. If your email address is not authorized, please contact support to get access.", + "login": { + "heading": "You are not logged in!", + "body": "You must be logged in to use this feature. Please log in, then try again." + } }, "alert": { "title": "Transcription Failed", @@ -357,7 +364,11 @@ "notAdminOrOwner": { "heading": "Restricted Access", "body": "For security reasons, only the meeting creator or an admin can start a recording (beta).", - "linkMore": "Learn more" + "linkMore": "Learn more", + "login": { + "heading": "Login Required", + "body": "Only the meeting creator or an admin can start screen recording. Log in to verify your permissions." + } }, "stopping": { "heading": "Saving your data…", diff --git a/src/frontend/src/locales/fr/rooms.json b/src/frontend/src/locales/fr/rooms.json index 09e7c2fe..fd79ca06 100644 --- a/src/frontend/src/locales/fr/rooms.json +++ b/src/frontend/src/locales/fr/rooms.json @@ -320,7 +320,11 @@ "notAdminOrOwner": { "heading": "Accès restreint", "body": "Pour des raisons de sécurité, seul le créateur ou un administrateur de la réunion peut lancer une transcription (beta).", - "linkMore": "En savoir plus" + "linkMore": "En savoir plus", + "login": { + "heading": "Connexion requise", + "body": "Seul le créateur de la réunion ou un administrateur peut démarrer la transcription. Connectez-vous pour vérifier vos autorisations." + } }, "stop": { "heading": "Transcription en cours …", @@ -331,10 +335,13 @@ "heading": "Sauvegarde de vos données…", "body": "Vous pouvez quitter la réunion si vous le souhaitez, la sauvegarde se terminera automatiquement." }, - "beta": { - "heading": "Devenez beta testeur", - "body": "Enregistrer votre réunion pour plus tard. Vous recevrez un compte-rendu par email une fois la réunion terminée.", - "button": "Inscrivez-vous" + "premium": { + "heading": "Fonctionnalité premium", + "body": "Cette fonctionnalité est réservée aux agents publics. Si votre adresse email n’est pas autorisée, contactez le support pour obtenir l'accès.", + "login": { + "heading": "Vous n'êtes pas connecté !", + "body": "Vous devez être connecté pour utiliser cette fonctionnalité. Connectez-vous, puis réessayez." + } }, "alert": { "title": "Échec de transcription", @@ -357,7 +364,11 @@ "notAdminOrOwner": { "heading": "Accès restreint", "body": "Pour des raisons de sécurité, seul le créateur ou un administrateur de la réunion peut lancer un enregistrement (beta).", - "linkMore": "En savoir plus" + "linkMore": "En savoir plus", + "login": { + "heading": "Connexion requise", + "body": "Seul le créateur de la réunion ou un administrateur peut démarrer l'enregistrement. Connectez-vous pour vérifier vos autorisations." + } }, "stopping": { "heading": "Sauvegarde de vos données…", diff --git a/src/frontend/src/locales/nl/rooms.json b/src/frontend/src/locales/nl/rooms.json index 95a70fa3..da22678c 100644 --- a/src/frontend/src/locales/nl/rooms.json +++ b/src/frontend/src/locales/nl/rooms.json @@ -320,7 +320,11 @@ "notAdminOrOwner": { "heading": "Toegang beperkt", "body": "Om veiligheidsredenen kan alleen de maker of een beheerder van de vergadering een transcriptie starten (beta).", - "linkMore": "Meer informatie" + "linkMore": "Meer informatie", + "login": { + "heading": "Inloggen vereist", + "body": "Alleen de maker van de vergadering of een beheerder kan de transcriptie starten. Log in om uw machtigingen te controleren." + } }, "stop": { "heading": "Transcriptie bezig...", @@ -331,10 +335,13 @@ "heading": "Uw gegevens worden opgeslagen…", "body": "U kunt de vergadering verlaten als u dat wilt; de opname wordt automatisch voltooid." }, - "beta": { - "heading": "Word betatester", - "body": "Neem uw vergadering op voor later. U ontvangt een samenvatting per e-mail zodra de vergadering is afgelopen.", - "button": "Aanmelden" + "premium": { + "heading": "Premiumfunctie", + "body": "Deze functie is voorbehouden aan openbare medewerkers. Als uw e-mailadres niet is toegestaan, neem dan contact op met de support om toegang te krijgen.", + "login": { + "heading": "Inloggen vereist", + "body": "Alleen de maker van de vergadering of een beheerder kan de transcriptie starten. Log in om uw machtigingen te controleren." + } }, "alert": { "title": "Transcriptie mislukt", @@ -357,7 +364,11 @@ "notAdminOrOwner": { "heading": "Toegang beperkt", "body": "Om veiligheidsredenen kan alleen de maker of een beheerder van de vergadering een opname starten (beta).", - "linkMore": "Meer informatie" + "linkMore": "Meer informatie", + "login": { + "heading": "Inloggen vereist", + "body": "Alleen de maker van de vergadering of een beheerder kan de opname starten. Log in om uw machtigingen te controleren." + } }, "stopping": { "heading": "Uw gegevens worden opgeslagen…", diff --git a/src/helm/env.d/dev-dinum/values.meet.yaml.gotmpl b/src/helm/env.d/dev-dinum/values.meet.yaml.gotmpl index c7bcaf13..7f7b6181 100644 --- a/src/helm/env.d/dev-dinum/values.meet.yaml.gotmpl +++ b/src/helm/env.d/dev-dinum/values.meet.yaml.gotmpl @@ -52,7 +52,6 @@ backend: ALLOW_UNREGISTERED_ROOMS: False FRONTEND_SILENCE_LIVEKIT_DEBUG: False FRONTEND_SUPPORT: "{'id': '58ea6697-8eba-4492-bc59-ad6562585041', 'help_article_transcript': 'https://lasuite.crisp.help/fr/article/visio-transcript-1sjq43x', 'help_article_recording': 'https://lasuite.crisp.help/fr/article/visio-enregistrement-wgc8o0', 'help_article_more_tools': 'https://lasuite.crisp.help/fr/article/visio-tools-bvxj23'}" - FRONTEND_TRANSCRIPT: "{'form_beta_users': 'https://grist.numerique.gouv.fr/o/docs/forms/3fFfvJoTBEQ6ZiMi8zsQwX/17'}" FRONTEND_FEEDBACK: "{'url': 'https://grist.numerique.gouv.fr/o/docs/cbMv4G7pLY3Z/USER-RESEARCH-or-LA-SUITE/f/26'}" FRONTEND_CUSTOM_CSS_URL: './assets/dinum-styles.css' FRONTEND_USE_FRENCH_GOV_FOOTER: True diff --git a/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl b/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl index cb1bbce2..12079fa3 100644 --- a/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl +++ b/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl @@ -54,7 +54,6 @@ backend: ALLOW_UNREGISTERED_ROOMS: False FRONTEND_SILENCE_LIVEKIT_DEBUG: False FRONTEND_SUPPORT: "{'id': '58ea6697-8eba-4492-bc59-ad6562585041', 'help_article_transcript': 'https://lasuite.crisp.help/fr/article/visio-transcript-1sjq43x', 'help_article_recording': 'https://lasuite.crisp.help/fr/article/visio-enregistrement-wgc8o0', 'help_article_more_tools': 'https://lasuite.crisp.help/fr/article/visio-tools-bvxj23'}" - FRONTEND_TRANSCRIPT: "{'form_beta_users': 'https://grist.numerique.gouv.fr/o/docs/forms/3fFfvJoTBEQ6ZiMi8zsQwX/17'}" FRONTEND_FEEDBACK: "{'url': 'https://grist.numerique.gouv.fr/o/docs/cbMv4G7pLY3Z/USER-RESEARCH-or-LA-SUITE/f/26'}" FRONTEND_MANIFEST_LINK: "https://docs.numerique.gouv.fr/docs/1ef86abf-f7e0-46ce-b6c7-8be8b8af4c3d/" FRONTEND_IDLE_DISCONNECT_WARNING_DELAY: 9000