Lot J : interrupteur de collecte des questions dans l'écran Guide IA
Sans UI, le client ne pouvait pas exercer le refus dont il est pourtant responsable. Le libellé dit ce que couper coûte (l'onglet cesse de se remplir) et ce que couper ne fait pas : rien n'est effacé, les questions déjà enregistrées vivent jusqu'à leur purge à 90 jours. manager_api_new étendu à la main. flutter build web vert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
82cb4cff3e
commit
9af4f8e2d8
@ -46,6 +46,9 @@ class _GuideIaScreenState extends State<GuideIaScreen> {
|
||||
/// Nul tant que le serveur n'a pas répondu : la ligne « ~N questions » ne s'affiche pas.
|
||||
int? _tokensPerQuestion;
|
||||
|
||||
/// Le client autorise-t-il l'enregistrement des questions de ses visiteurs ? (RGPD)
|
||||
bool _collectionEnabled = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -85,6 +88,7 @@ class _GuideIaScreenState extends State<GuideIaScreen> {
|
||||
_nameController.text = instance?.guideName ?? '';
|
||||
_personaController.text = instance?.guidePersonaPrompt ?? '';
|
||||
_voiceId = instance?.guideVoiceId ?? kGuideVoiceViva;
|
||||
_collectionEnabled = instance?.isVisitorQuestionCollectionEnabled ?? true;
|
||||
|
||||
final ownLanguage = (instance?.guideFallbackMessages ?? [])
|
||||
.where((m) => m.language == _editingLanguage && (m.value ?? '').isNotEmpty);
|
||||
@ -186,6 +190,7 @@ class _GuideIaScreenState extends State<GuideIaScreen> {
|
||||
instance.guideName = _nameController.text.trim();
|
||||
instance.guidePersonaPrompt = _personaController.text.trim();
|
||||
instance.guideVoiceId = _voiceId;
|
||||
instance.isVisitorQuestionCollectionEnabled = _collectionEnabled;
|
||||
instance.guideFallbackMessages = [...otherLanguages, ...edited];
|
||||
|
||||
try {
|
||||
@ -332,6 +337,8 @@ class _GuideIaScreenState extends State<GuideIaScreen> {
|
||||
const SizedBox(height: 16),
|
||||
_channelsCard(l),
|
||||
const SizedBox(height: 16),
|
||||
_collectionCard(l),
|
||||
const SizedBox(height: 16),
|
||||
_identityCard(l),
|
||||
],
|
||||
);
|
||||
@ -398,6 +405,43 @@ class _GuideIaScreenState extends State<GuideIaScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
/// Interrupteur de collecte (RGPD, lot J).
|
||||
///
|
||||
/// ⚠️ **C'est le client qui est responsable de traitement**, pas nous : il doit pouvoir
|
||||
/// refuser. La collecte était inconditionnelle dès que l'assistant était actif.
|
||||
///
|
||||
/// Le texte dit ce que couper coûte — sans quoi le gestionnaire le désactive « par
|
||||
/// prudence » puis s'étonne que son onglet reste vide — et ce que couper ne fait pas :
|
||||
/// les questions déjà enregistrées vivent jusqu'à leur purge, ce n'est pas un effacement.
|
||||
Widget _collectionCard(AppLocalizations l) {
|
||||
return _card(
|
||||
title: 'Questions de vos visiteurs',
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SwitchListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
value: _collectionEnabled,
|
||||
onChanged: (v) => setState(() => _collectionEnabled = v),
|
||||
title: const Text('Enregistrer les questions posées au guide',
|
||||
style: TextStyle(fontSize: 14)),
|
||||
subtitle: Text(
|
||||
_collectionEnabled
|
||||
? 'Alimente l\'onglet « Ce que demandent vos visiteurs » et le rapport des '
|
||||
'questions restées sans réponse. Conservation 90 jours, puis seuls les '
|
||||
'regroupements par thème sont gardés.'
|
||||
: 'Aucune nouvelle question ne sera enregistrée. L\'onglet « Ce que demandent '
|
||||
'vos visiteurs » cessera de se remplir. Les questions déjà enregistrées '
|
||||
'restent jusqu\'à leur suppression automatique à 90 jours.',
|
||||
style: TextStyle(fontSize: 12, color: kBodyTextColor),
|
||||
),
|
||||
activeThumbColor: kPrimaryColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _card({required String title, String? subtitle, required Widget child}) {
|
||||
return Card(
|
||||
elevation: 0,
|
||||
|
||||
@ -26,6 +26,7 @@ class InstanceDTO {
|
||||
this.guideName,
|
||||
this.guidePersonaPrompt,
|
||||
this.guideVoiceId,
|
||||
this.isVisitorQuestionCollectionEnabled,
|
||||
this.guideFallbackMessages = const [],
|
||||
this.subscriptionPlanId,
|
||||
this.subscriptionPlan,
|
||||
@ -109,6 +110,10 @@ class InstanceDTO {
|
||||
/// Voix Gemini TTS : "Sulafat" (Viva) ou "Umbriel" (Marco).
|
||||
String? guideVoiceId;
|
||||
|
||||
/// Le client autorise-t-il l'enregistrement des questions de ses visiteurs ? (RGPD)
|
||||
/// Couper la collecte n'efface rien : les questions déjà là vivent jusqu'à leur purge.
|
||||
bool? isVisitorQuestionCollectionEnabled;
|
||||
|
||||
/// Formulations de repli. Liste à plat : plusieurs entrées peuvent partager la même langue.
|
||||
List<TranslationDTO> guideFallbackMessages;
|
||||
|
||||
@ -264,6 +269,11 @@ class InstanceDTO {
|
||||
} else {
|
||||
json[r'guideVoiceId'] = null;
|
||||
}
|
||||
if (this.isVisitorQuestionCollectionEnabled != null) {
|
||||
json[r'isVisitorQuestionCollectionEnabled'] = this.isVisitorQuestionCollectionEnabled;
|
||||
} else {
|
||||
json[r'isVisitorQuestionCollectionEnabled'] = null;
|
||||
}
|
||||
json[r'guideFallbackMessages'] = this.guideFallbackMessages;
|
||||
if (this.subscriptionPlanId != null) {
|
||||
json[r'subscriptionPlanId'] = this.subscriptionPlanId;
|
||||
@ -372,6 +382,8 @@ class InstanceDTO {
|
||||
guideName: mapValueOfType<String>(json, r'guideName'),
|
||||
guidePersonaPrompt: mapValueOfType<String>(json, r'guidePersonaPrompt'),
|
||||
guideVoiceId: mapValueOfType<String>(json, r'guideVoiceId'),
|
||||
isVisitorQuestionCollectionEnabled:
|
||||
mapValueOfType<bool>(json, r'isVisitorQuestionCollectionEnabled'),
|
||||
guideFallbackMessages: TranslationDTO.listFromJson(json[r'guideFallbackMessages']),
|
||||
subscriptionPlanId: mapValueOfType<String>(json, r'subscriptionPlanId'),
|
||||
subscriptionPlan: SubscriptionPlanDTO.fromJson(json[r'subscriptionPlan']),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user