diff --git a/src/app/[slug]/[configId]/page.tsx b/src/app/[slug]/[configId]/page.tsx
index 9cdcae0..b6b09e4 100644
--- a/src/app/[slug]/[configId]/page.tsx
+++ b/src/app/[slug]/[configId]/page.tsx
@@ -3,6 +3,7 @@ import { getInstanceBySlug, getConfiguration, getSections } from '@/lib/api/clie
import SectionList from '@/components/SectionList'
import QRScannerButton from '@/components/QRScannerButton'
import ProximitySuggestion from '@/components/ProximitySuggestion'
+import CastingIntro from '@/components/CastingIntro'
export default async function ConfigPage({
params,
@@ -39,6 +40,7 @@ export default async function ConfigPage({
configPrimaryColor={config.primaryColor}
languages={config.languages ?? ['FR']}
/>
+ {config.casting && config.casting.length >= 2 && }
{instance.isQRCodeEnabled !== false && }
>
diff --git a/src/components/CastingIntro.tsx b/src/components/CastingIntro.tsx
new file mode 100644
index 0000000..a27a426
--- /dev/null
+++ b/src/components/CastingIntro.tsx
@@ -0,0 +1,73 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import Image from 'next/image'
+import { useVisitor } from '@/context/VisitorContext'
+import { ui } from '@/lib/i18n'
+import type { CastingVisitorMemberDTO } from '@/lib/api/types'
+
+/**
+ * Écran d'entrée « les personnes que vous allez rencontrer » (plan Studio décision 25). Le serveur
+ * ne l'envoie qu'avec l'interrupteur armé et au moins deux portraits. Montré une fois par visite :
+ * le visiteur qui revient à la liste des sections ne le revoit pas.
+ */
+export default function CastingIntro({ casting, configId }: { casting: CastingVisitorMemberDTO[]; configId: string }) {
+ const { language } = useVisitor()
+ const [open, setOpen] = useState(false)
+ const storageKey = `casting-seen-${configId}`
+
+ useEffect(() => {
+ try {
+ if (sessionStorage.getItem(storageKey)) return
+ } catch {
+ // Stockage indisponible (navigation privée) : on montre l'écran, sans mémoriser.
+ }
+ setOpen(true)
+ }, [storageKey])
+
+ function close() {
+ try {
+ sessionStorage.setItem(storageKey, '1')
+ } catch {
+ // Idem : l'écran reviendra à la prochaine ouverture, ce n'est pas grave.
+ }
+ setOpen(false)
+ }
+
+ if (!open) return null
+
+ return (
+
+
+
+ {ui('castingTitle', language)}
+
+
+ {casting.map((member, index) => (
+
+
+ {member.name}
+ {member.role && {member.role}}
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/src/lib/api/types.ts b/src/lib/api/types.ts
index 0d90fcb..bc17d8d 100644
--- a/src/lib/api/types.ts
+++ b/src/lib/api/types.ts
@@ -96,6 +96,14 @@ export interface ConfigurationDTO {
isOffline?: boolean
sectionIds?: string[]
sections?: SectionDTO[]
+ /** Casting montre au visiteur (decision 25). Absent tant que l'ecran d'entree ne doit pas s'afficher. */
+ casting?: CastingVisitorMemberDTO[]
+}
+
+export interface CastingVisitorMemberDTO {
+ name: string
+ role?: string
+ portraitUrl: string
}
export interface AppConfigurationLinkDTO {
diff --git a/src/lib/i18n.ts b/src/lib/i18n.ts
index 4208a32..ecdb713 100644
--- a/src/lib/i18n.ts
+++ b/src/lib/i18n.ts
@@ -24,6 +24,32 @@ const UI_STRINGS: Record> = {
UK: 'Зображення, створене ШІ',
LB: 'Bild generéiert vun der KI',
},
+ castingTitle: {
+ FR: 'Les personnes que vous allez rencontrer',
+ NL: 'De personen die u zult ontmoeten',
+ EN: 'The people you will meet',
+ DE: 'Die Menschen, denen Sie begegnen werden',
+ ES: 'Las personas que va a conocer',
+ IT: 'Le persone che incontrerete',
+ PL: 'Osoby, które spotkasz',
+ CN: '您将遇到的人物',
+ AR: 'الشخصيات التي ستلتقيها',
+ UK: 'Люди, яких ви зустрінете',
+ LB: "D'Leit, déi Dir kenne léiert",
+ },
+ castingStart: {
+ FR: 'Commencer la visite',
+ NL: 'Het bezoek beginnen',
+ EN: 'Start the visit',
+ DE: 'Besuch beginnen',
+ ES: 'Empezar la visita',
+ IT: 'Inizia la visita',
+ PL: 'Rozpocznij zwiedzanie',
+ CN: '开始参观',
+ AR: 'ابدأ الزيارة',
+ UK: 'Почати відвідування',
+ LB: 'Besuch ufänken',
+ },
noEventsThisMonth: {
FR: 'Aucun événement ce mois-ci',
NL: 'Geen evenementen deze maand',