Files
social-app/apps/lib/features/settings/presentation/screens/features_screen.dart
T

233 lines
7.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import '../../../../app/di/injection.dart';
import '../../../../app/router/app_routes.dart';
import '../../../../core/l10n/l10n.dart';
import '../../../../core/theme/design_tokens.dart';
import '../../../../shared/widgets/app_button.dart';
import '../../../../shared/widgets/app_loading_indicator.dart';
import '../../../../shared/widgets/app_pressable.dart';
import '../../../../shared/widgets/app_toggle_switch.dart';
import '../../../../shared/widgets/toast/toast.dart';
import '../../../../shared/widgets/toast/toast_type.dart';
import '../../data/models/automation_job_model.dart';
import '../../data/apis/automation_jobs_api.dart';
import '../../presentation/cubits/automation_jobs_cubit.dart';
import '../widgets/settings_page_scaffold.dart';
class FeaturesScreen extends StatefulWidget {
const FeaturesScreen({super.key});
@override
State<FeaturesScreen> createState() => _FeaturesScreenState();
}
class _FeaturesScreenState extends State<FeaturesScreen> {
late final AutomationJobsCubit _cubit;
@override
void initState() {
super.initState();
_cubit = AutomationJobsCubit(sl<AutomationJobsApi>());
_cubit.loadJobs();
}
@override
void dispose() {
_cubit.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return BlocProvider.value(
value: _cubit,
child: SettingsPageScaffold(
title: context.l10n.settingsFeaturesTitle,
onBack: () => context.pop(),
body: BlocBuilder<AutomationJobsCubit, AutomationJobsState>(
builder: (context, state) {
if (state.isLoading) {
return const Center(
child: AppLoadingIndicator(variant: AppLoadingVariant.surface),
);
}
if (state.error != null) {
return Center(child: Text(state.error!));
}
return _buildJobList(state);
},
),
),
);
}
Widget _buildJobList(AutomationJobsState state) {
final dailyJobs = state.dailyJobs;
final weeklyJobs = state.weeklyJobs;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionTitle(context.l10n.settingsSectionDaily),
const SizedBox(height: AppSpacing.sm),
if (dailyJobs.isEmpty)
_buildEmptyHint(context.l10n.settingsNoDailyPlans)
else
...dailyJobs.map(_buildJobCard),
const SizedBox(height: AppSpacing.lg),
_buildSectionTitle(context.l10n.settingsSectionWeekly),
const SizedBox(height: AppSpacing.sm),
if (weeklyJobs.isEmpty)
_buildEmptyHint(context.l10n.settingsNoWeeklyPlans)
else
...weeklyJobs.map(_buildJobCard),
if (state.canCreateMore) ...[
const SizedBox(height: AppSpacing.lg),
_buildCreateButton(),
],
],
);
}
Widget _buildEmptyHint(String text) {
final colorScheme = Theme.of(context).colorScheme;
return Container(
width: double.infinity,
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Text(
text,
style: TextStyle(
color: colorScheme.onSurfaceVariant,
fontSize: 13,
fontWeight: FontWeight.w500,
),
),
);
}
Widget _buildSectionTitle(String title) {
final colorScheme = Theme.of(context).colorScheme;
return Text(
title,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: colorScheme.onSurfaceVariant,
),
);
}
Widget _buildJobCard(AutomationJobModel job) {
final colorScheme = Theme.of(context).colorScheme;
return AppPressable(
onTap: () async {
await context.push(AppRoutes.settingsJobDetail(job.id));
if (!mounted) {
return;
}
_cubit.loadJobs();
},
borderRadius: BorderRadius.circular(AppRadius.lg),
child: Container(
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Row(
children: [
Container(
width: AppSpacing.xxl + AppSpacing.lg,
height: AppSpacing.xxl + AppSpacing.lg,
decoration: BoxDecoration(
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Icon(
Icons.auto_awesome,
size: AppSpacing.lg,
color: colorScheme.primary,
),
),
const SizedBox(width: AppSpacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
job.title,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: colorScheme.onSurface,
),
),
const SizedBox(height: AppSpacing.xs),
Text(
_buildSubtitle(job),
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurfaceVariant,
),
),
],
),
),
const SizedBox(width: AppSpacing.sm),
AppToggleSwitch(
value: job.isActive,
onChanged: (next) {
if (job.isSystem) {
Toast.show(
context,
context.l10n.settingsSystemJobReadonly,
type: ToastType.info,
);
return;
}
_cubit.updateJobStatus(id: job.id, enabled: next);
},
),
],
),
),
);
}
String _buildSubtitle(AutomationJobModel job) {
final statusText = job.isActive
? context.l10n.settingsJobStatusEnabled
: context.l10n.settingsJobStatusDisabled;
final sourceText = job.isSystem
? context.l10n.settingsJobSourceSystem
: context.l10n.settingsJobSourceCustom;
return '$sourceText$statusText';
}
Widget _buildCreateButton() {
return AppButton(
text: context.l10n.settingsCreateJob,
onPressed: () async {
await context.push(AppRoutes.settingsJobNew);
if (!mounted) {
return;
}
_cubit.loadJobs();
},
);
}
}