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