From 8979ab2e635271a1171d1947995689dc86f18192 Mon Sep 17 00:00:00 2001 From: Thomas Fransolet Date: Tue, 15 Sep 2026 22:41:40 +0200 Subject: [PATCH] =?UTF-8?q?Studio=20lot=208bis=20:=20pr=C3=A9senter=20les?= =?UTF-8?q?=20personnages=20au=20visiteur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Écran d'entrée au chargement de la configuration. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_011VxSQeGQYUvPmSEoGdnidA --- lib/Components/casting_intro_dialog.dart | 118 +++++++++++++++++++++++ lib/Screens/MainView/main_view.dart | 6 ++ 2 files changed, 124 insertions(+) create mode 100644 lib/Components/casting_intro_dialog.dart diff --git a/lib/Components/casting_intro_dialog.dart b/lib/Components/casting_intro_dialog.dart new file mode 100644 index 0000000..6602a7c --- /dev/null +++ b/lib/Components/casting_intro_dialog.dart @@ -0,0 +1,118 @@ +import 'package:flutter/material.dart'; +import 'package:manager_api_new/api.dart'; + +/// Écran d'entrée « les personnes que vous allez rencontrer » (plan Studio décision 25). +/// +/// Le serveur n'envoie le casting qu'avec l'interrupteur armé et au moins deux portraits. Montré une +/// fois par configuration et par lancement de l'app : revenir à la liste ne le rouvre pas. Comme la +/// mention IA, les libellés sont portés ici — l'app n'a pas de traductions pour ses propres textes. +class CastingIntroDialog extends StatelessWidget { + const CastingIntroDialog({Key? key, required this.casting, required this.language}) : super(key: key); + + final List casting; + final String? language; + + static final Set _shown = {}; + + static const _titles = { + '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", + }; + + static const _starts = { + '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', + }; + + static void showIfNeeded(BuildContext context, ConfigurationDTO configuration, String? language) { + final casting = configuration.casting; + if (casting == null || casting.length < 2 || configuration.id == null) return; + if (!_shown.add(configuration.id!)) return; + showDialog( + context: context, + builder: (_) => CastingIntroDialog(casting: casting, language: language), + ); + } + + String _label(Map labels) => labels[(language ?? 'FR').toUpperCase()] ?? labels['FR']!; + + @override + Widget build(BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(22)), + child: Padding( + padding: const EdgeInsets.fromLTRB(20, 24, 20, 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + _label(_titles), + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w700, color: Color(0xFF1E2A33)), + ), + const SizedBox(height: 20), + Wrap( + alignment: WrapAlignment.center, + spacing: 16, + runSpacing: 16, + children: casting + .map((member) => SizedBox( + width: 110, + child: Column( + children: [ + ClipOval( + child: SizedBox( + width: 88, + height: 88, + child: Image.network( + member.portraitUrl ?? '', + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Container(color: const Color(0xFFE8E3DA)), + ), + ), + ), + const SizedBox(height: 6), + Text(member.name ?? '', + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600)), + if ((member.role ?? '').isNotEmpty) + Text(member.role!, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 12, color: Color(0xFF6B7B86))), + ], + ), + )) + .toList(), + ), + const SizedBox(height: 24), + SizedBox( + width: double.infinity, + child: FilledButton( + onPressed: () => Navigator.of(context).pop(), + child: Text(_label(_starts)), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Screens/MainView/main_view.dart b/lib/Screens/MainView/main_view.dart index 3303aee..9330aa2 100644 --- a/lib/Screens/MainView/main_view.dart +++ b/lib/Screens/MainView/main_view.dart @@ -1,3 +1,4 @@ +import 'package:tablet_app/Components/casting_intro_dialog.dart'; import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -328,6 +329,11 @@ class _MainViewWidget extends State { await getCurrentConfiguration(appContext); + final loadedConfiguration = tabletAppContext.configuration; + if (mounted && loadedConfiguration != null) { + CastingIntroDialog.showIfNeeded(context, loadedConfiguration, tabletAppContext.language); + } + // Load applicationInstanceDTO to check if assistant/statistics are enabled try { if (tabletAppContext.instanceId != null) {