11import  'package:core/core.dart' ;
22import  'package:flutter/material.dart' ;
3+ import  'package:flutter_news_app_web_dashboard_full_source_code/l10n/app_localizations.dart' ;
34import  'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart' ;
45import  'package:flutter_news_app_web_dashboard_full_source_code/shared/extensions/banner_ad_shape_l10n.dart' ;
56import  'package:flutter_news_app_web_dashboard_full_source_code/shared/extensions/in_article_ad_slot_type_l10n.dart' ;
7+ import  'package:flutter_news_app_web_dashboard_full_source_code/shared/extensions/app_user_role_l10n.dart' ;
68import  'package:ui_kit/ui_kit.dart' ;
79
810/// {@template article_ad_settings_form} 
@@ -28,14 +30,28 @@ class ArticleAdSettingsForm extends StatefulWidget {
2830
2931class  _ArticleAdSettingsFormState  extends  State <ArticleAdSettingsForm >
3032 with  SingleTickerProviderStateMixin  {
33+  late  TabController  _tabController;
34+ 3135 @override 
3236 void  initState () {
3337 super .initState ();
38+  _tabController =  TabController (
39+  length:  AppUserRole .values.length,
40+  vsync:  this ,
41+  );
3442 }
3543
3644 @override 
3745 void  didUpdateWidget (covariant  ArticleAdSettingsForm  oldWidget) {
3846 super .didUpdateWidget (oldWidget);
47+  // No specific controller updates needed here as the UI rebuilds based on 
48+  // the remoteConfig directly. 
49+  }
50+ 51+  @override 
52+  void  dispose () {
53+  _tabController.dispose ();
54+  super .dispose ();
3955 }
4056
4157 @override 
@@ -134,34 +150,129 @@ class _ArticleAdSettingsFormState extends State<ArticleAdSettingsForm>
134150 textAlign:  TextAlign .start,
135151 ),
136152 const  SizedBox (height:  AppSpacing .lg),
137-  ...articleAdConfig.inArticleAdSlotConfigurations.map (
138-  (slotConfig) =>  SwitchListTile (
139-  title:  Text (slotConfig.slotType.l10n (context)),
140-  value:  slotConfig.enabled,
141-  onChanged:  (value) {
142-  final  updatedSlots =  articleAdConfig
143-  .inArticleAdSlotConfigurations
144-  .map (
145-  (e) =>  e.slotType ==  slotConfig.slotType
146-  ?  e.copyWith (enabled:  value)
147-  :  e,
148-  )
149-  .toList ();
150-  widget.onConfigChanged (
151-  widget.remoteConfig.copyWith (
152-  adConfig:  adConfig.copyWith (
153-  articleAdConfiguration:  articleAdConfig.copyWith (
154-  inArticleAdSlotConfigurations:  updatedSlots,
155-  ),
153+  Align (
154+  alignment:  AlignmentDirectional .centerStart,
155+  child:  SizedBox (
156+  height:  kTextTabBarHeight,
157+  child:  TabBar (
158+  controller:  _tabController,
159+  tabAlignment:  TabAlignment .start,
160+  isScrollable:  true ,
161+  tabs:  AppUserRole .values
162+  .map ((role) =>  Tab (text:  role.l10n (context)))
163+  .toList (),
164+  ),
165+  ),
166+  ),
167+  const  SizedBox (height:  AppSpacing .lg),
168+  SizedBox (
169+  height:  250 ,
170+  child:  TabBarView (
171+  controller:  _tabController,
172+  children:  AppUserRole .values
173+  .map (
174+  (role) =>  _buildRoleSpecificFields (
175+  context,
176+  l10n,
177+  role,
178+  articleAdConfig,
156179 ),
157-  ),
158-  );
159-  },
180+  )
181+  .toList (),
160182 ),
161183 ),
162184 ],
163185 ),
164186 ],
165187 );
166188 }
189+ 190+  /// Builds role-specific configuration fields for in-article ad slots. 
191+  /// 
192+  /// This widget displays checkboxes for each [InArticleAdSlotType]  for a 
193+  /// given [AppUserRole] , allowing to enable/disable specific ad slots. 
194+  Widget  _buildRoleSpecificFields (
195+  BuildContext  context,
196+  AppLocalizations  l10n,
197+  AppUserRole  role,
198+  ArticleAdConfiguration  config,
199+  ) {
200+  final  roleSlots =  config.visibleTo[role];
201+ 202+  return  Column (
203+  children:  [
204+  SwitchListTile (
205+  title:  Text (l10n.enableInArticleAdsForRoleLabel (role.l10n (context))),
206+  value:  roleSlots !=  null ,
207+  onChanged:  (value) {
208+  final  newVisibleTo = 
209+  Map <AppUserRole , Map <InArticleAdSlotType , bool >>.from (
210+  config.visibleTo,
211+  );
212+  if  (value) {
213+  // Default values when enabling for a role 
214+  newVisibleTo[role] =  {
215+  InArticleAdSlotType .aboveArticleContinueReadingButton:  true ,
216+  InArticleAdSlotType .belowArticleContinueReadingButton:  true ,
217+  };
218+  } else  {
219+  newVisibleTo.remove (role);
220+  }
221+ 222+  widget.onConfigChanged (
223+  widget.remoteConfig.copyWith (
224+  adConfig:  widget.remoteConfig.adConfig.copyWith (
225+  articleAdConfiguration:  config.copyWith (
226+  visibleTo:  newVisibleTo,
227+  ),
228+  ),
229+  ),
230+  );
231+  },
232+  ),
233+  if  (roleSlots !=  null )
234+  Padding (
235+  padding:  const  EdgeInsets .symmetric (
236+  horizontal:  AppSpacing .lg,
237+  vertical:  AppSpacing .sm,
238+  ),
239+  child:  Column (
240+  children:  [
241+  // SwitchListTile for each InArticleAdSlotType 
242+  for  (final  slotType in  InArticleAdSlotType .values)
243+  CheckboxListTile (
244+  title:  Text (slotType.l10n (context)),
245+  value:  roleSlots[slotType] ??  false ,
246+  onChanged:  (value) {
247+  final  newRoleSlots =  Map <InArticleAdSlotType , bool >.from (
248+  roleSlots,
249+  );
250+  if  (value ??  false ) {
251+  newRoleSlots[slotType] =  true ;
252+  } else  {
253+  newRoleSlots.remove (slotType);
254+  }
255+ 256+  final  newVisibleTo = 
257+  Map <AppUserRole , Map <InArticleAdSlotType , bool >>.from (
258+  config.visibleTo,
259+  )..[role] =  newRoleSlots;
260+ 261+  widget.onConfigChanged (
262+  widget.remoteConfig.copyWith (
263+  adConfig:  widget.remoteConfig.adConfig.copyWith (
264+  articleAdConfiguration:  config.copyWith (
265+  visibleTo:  newVisibleTo,
266+  ),
267+  ),
268+  ),
269+  );
270+  },
271+  ),
272+  ],
273+  ),
274+  ),
275+  ],
276+  );
277+  }
167278}
0 commit comments