(front) add video conference link generation in event modal

Implement the visio conference feature in the event modal:
- Add NEXT_PUBLIC_VISIO_BASE_URL env var for configurable base URL
- Create generateVisioRoomId() utility (xxx-xxxx-xxx format, a-z)
- Refactor VideoConferenceSection: button to create, link + remove
  when URL exists
- Hide visio pill when env var is not configured (feature flag)
- Add removeVisio i18n key in EN/FR/NL

The visio URL is stored via the standard ICS URL property, which
is already wired through useEventForm.toIcsEvent() and CalDavService.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Nathan Panchout
2026-02-06 17:38:57 +01:00
parent 5c86f79192
commit eac8cde272
6 changed files with 59 additions and 23 deletions

View File

@@ -1 +1,2 @@
NEXT_PUBLIC_API_ORIGIN= NEXT_PUBLIC_API_ORIGIN=
NEXT_PUBLIC_VISIO_BASE_URL=https://visio.suite.anct.gouv.fr

View File

@@ -1 +1,2 @@
NEXT_PUBLIC_API_ORIGIN=http://localhost:8921 NEXT_PUBLIC_API_ORIGIN=http://localhost:8921
NEXT_PUBLIC_VISIO_BASE_URL=https://visio.suite.anct.gouv.fr

View File

@@ -119,13 +119,19 @@ export const EventModal = ({
} }
}; };
const visioBaseUrl = process.env.NEXT_PUBLIC_VISIO_BASE_URL;
const pills = useMemo( const pills = useMemo(
() => [ () => [
{ ...(visioBaseUrl
id: "videoConference" as const, ? [
icon: "videocam", {
label: t("calendar.event.sections.addVideoConference"), id: "videoConference" as const,
}, icon: "videocam",
label: t("calendar.event.sections.addVideoConference"),
},
]
: []),
{ {
id: "location" as const, id: "location" as const,
icon: "place", icon: "place",
@@ -147,7 +153,7 @@ export const EventModal = ({
label: t("calendar.event.attendees"), label: t("calendar.event.attendees"),
}, },
], ],
[t], [t, visioBaseUrl],
); );
return ( return (

View File

@@ -1,7 +1,7 @@
import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Button } from "@gouvfr-lasuite/cunningham-react"; import { Button } from "@gouvfr-lasuite/cunningham-react";
import { SectionRow } from "./SectionRow"; import { SectionRow } from "./SectionRow";
import { generateVisioRoomId } from "./generateVisioRoomId";
interface VideoConferenceSectionProps { interface VideoConferenceSectionProps {
url: string; url: string;
@@ -19,14 +19,12 @@ export const VideoConferenceSection = ({
onToggle, onToggle,
}: VideoConferenceSectionProps) => { }: VideoConferenceSectionProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [isCreating, setIsCreating] = useState(false);
const handleCreateVisio = () => { const handleCreateVisio = () => {
// Inert for now - will integrate with La Suite API in the future const baseUrl = process.env.NEXT_PUBLIC_VISIO_BASE_URL;
setIsCreating(true); if (!baseUrl) return;
setTimeout(() => { const roomId = generateVisioRoomId();
setIsCreating(false); onChange(`${baseUrl}/${roomId}`);
}, 500);
}; };
const handleRemove = () => { const handleRemove = () => {
@@ -42,15 +40,35 @@ export const VideoConferenceSection = ({
isExpanded={isExpanded} isExpanded={isExpanded}
onToggle={onToggle} onToggle={onToggle}
> >
<Button {url ? (
size="small" <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
color="neutral" <a
variant="tertiary" href={url}
onClick={handleCreateVisio} target="_blank"
disabled={isCreating} rel="noopener noreferrer"
> style={{ wordBreak: "break-all" }}
{t("calendar.event.sections.createVisio")} >
</Button> {url}
</a>
<Button
size="small"
color="neutral"
variant="tertiary"
icon={<span className="material-icons">close</span>}
onClick={handleRemove}
aria-label={t("calendar.event.sections.removeVisio")}
/>
</div>
) : (
<Button
size="small"
color="neutral"
variant="tertiary"
onClick={handleCreateVisio}
>
{t("calendar.event.sections.createVisio")}
</Button>
)}
</SectionRow> </SectionRow>
); );
}; };

View File

@@ -0,0 +1,7 @@
const randomLetters = (n: number): string =>
Array.from({ length: n }, () =>
String.fromCharCode(97 + Math.floor(Math.random() * 26)),
).join("");
export const generateVisioRoomId = (): string =>
`${randomLetters(3)}-${randomLetters(4)}-${randomLetters(3)}`;

View File

@@ -137,6 +137,7 @@
"addLocation": "Add location", "addLocation": "Add location",
"addVideoConference": "Visio", "addVideoConference": "Visio",
"createVisio": "Add video conference", "createVisio": "Add video conference",
"removeVisio": "Remove video conference",
"videoLink": "Video conference link", "videoLink": "Video conference link",
"addAttendees": "Add participants", "addAttendees": "Add participants",
"addDescription": "Add description", "addDescription": "Add description",
@@ -747,6 +748,7 @@
"addLocation": "Ajouter un lieu", "addLocation": "Ajouter un lieu",
"addVideoConference": "Visio", "addVideoConference": "Visio",
"createVisio": "Ajouter une visioconférence", "createVisio": "Ajouter une visioconférence",
"removeVisio": "Supprimer la visioconférence",
"videoLink": "Lien de visioconférence", "videoLink": "Lien de visioconférence",
"addAttendees": "Ajouter des participants", "addAttendees": "Ajouter des participants",
"addDescription": "Ajouter une description", "addDescription": "Ajouter une description",
@@ -1104,6 +1106,7 @@
"addLocation": "Locatie toevoegen", "addLocation": "Locatie toevoegen",
"addVideoConference": "Visio", "addVideoConference": "Visio",
"createVisio": "Add video conference", "createVisio": "Add video conference",
"removeVisio": "Videoconferentie verwijderen",
"videoLink": "Videoconferentie link", "videoLink": "Videoconferentie link",
"addAttendees": "Deelnemers toevoegen", "addAttendees": "Deelnemers toevoegen",
"addDescription": "Beschrijving toevoegen", "addDescription": "Beschrijving toevoegen",