Skip to content

Commit

Permalink
feat(Actions): ajouter sur la page d'accueil
Browse files Browse the repository at this point in the history
  • Loading branch information
lsaudon committed Dec 16, 2024
1 parent 61ed5a3 commit 786c836
Show file tree
Hide file tree
Showing 29 changed files with 451 additions and 105 deletions.
12 changes: 11 additions & 1 deletion app/lib/app/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import 'package:app/core/infrastructure/message_bus.dart';
import 'package:app/core/infrastructure/tracker.dart';
import 'package:app/features/accueil/presentation/cubit/home_disclaimer_cubit.dart';
import 'package:app/features/actions/detail/infrastructure/action_repository.dart';
import 'package:app/features/actions/home/infrastructure/home_actions_repository.dart';
import 'package:app/features/actions/home/presentation/bloc/home_actions_bloc.dart';
import 'package:app/features/actions/list/domain/actions_port.dart';
import 'package:app/features/actions/list/infrastructure/actions_adapter.dart';
import 'package:app/features/articles/domain/articles_port.dart';
import 'package:app/features/articles/infrastructure/articles_api_adapter.dart';
Expand Down Expand Up @@ -147,7 +150,7 @@ class _AppState extends State<App> {
create: (final context) =>
ArticlesApiAdapter(client: widget.dioHttpClient),
),
RepositoryProvider(
RepositoryProvider<ActionsPort>(
create: (final context) =>
ActionsAdapter(client: widget.dioHttpClient),
),
Expand Down Expand Up @@ -187,6 +190,13 @@ class _AppState extends State<App> {
MissionHomeRepository(client: widget.dioHttpClient),
),
),
BlocProvider(
create: (final context) => HomeActionsBloc(
repository: HomeActionsRepository(
client: widget.dioHttpClient,
),
),
),
BlocProvider(create: (final context) => AideBloc()),
BlocProvider(
create: (final context) =>
Expand Down
15 changes: 11 additions & 4 deletions app/lib/features/accueil/presentation/pages/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:app/core/presentation/widgets/fondamentaux/rounded_rectangle_bor
import 'package:app/core/presentation/widgets/fondamentaux/text_styles.dart';
import 'package:app/features/accueil/presentation/cubit/home_disclaimer_cubit.dart';
import 'package:app/features/accueil/presentation/cubit/home_disclaimer_state.dart';
import 'package:app/features/actions/home/presentation/widgets/actions_section.dart';
import 'package:app/features/assistances/core/presentation/widgets/assitances_section.dart';
import 'package:app/features/environmental_performance/home/presentation/widgets/environmental_performance_section.dart';
import 'package:app/features/environmental_performance/summary/presentation/bloc/environmental_performance_bloc.dart';
Expand All @@ -10,6 +11,7 @@ import 'package:app/features/first_name/presentation/pages/first_name_page.dart'
import 'package:app/features/menu/presentation/pages/root_page.dart';
import 'package:app/features/mission/home/presentation/widgets/mission_section.dart';
import 'package:app/features/survey/survey_section.dart';
import 'package:app/features/theme/core/domain/theme_type.dart';
import 'package:app/features/theme/presentation/pages/theme_page.dart';
import 'package:app/features/utilisateur/presentation/bloc/utilisateur_bloc.dart';
import 'package:app/features/utilisateur/presentation/bloc/utilisateur_event.dart';
Expand Down Expand Up @@ -74,10 +76,10 @@ class _TabPart extends StatelessWidget {
physics: NeverScrollableScrollPhysics(),
children: [
_Home(),
ThemePage(type: 'alimentation'),
ThemePage(type: 'logement'),
ThemePage(type: 'transport'),
ThemePage(type: 'consommation'),
ThemePage(themeType: ThemeType.alimentation),
ThemePage(themeType: ThemeType.logement),
ThemePage(themeType: ThemeType.transport),
ThemePage(themeType: ThemeType.consommation),
],
),
),
Expand Down Expand Up @@ -158,6 +160,11 @@ class _HomeState extends State<_Home> {
child: AssitancesSection(),
),
SizedBox(height: DsfrSpacings.s4w),
Padding(
padding: EdgeInsets.symmetric(horizontal: paddingVerticalPage),
child: ActionsSection(),
),
SizedBox(height: DsfrSpacings.s4w),
SurveySection(),
],
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'package:app/core/infrastructure/endpoints.dart';
import 'package:app/core/infrastructure/http_client_helpers.dart';
import 'package:app/features/actions/list/domain/action_item.dart';
import 'package:app/features/actions/list/infrastructure/action_item_mapper.dart';
import 'package:app/features/authentification/core/infrastructure/dio_http_client.dart';
import 'package:app/features/theme/core/domain/theme_type.dart';
import 'package:fpdart/fpdart.dart';

class HomeActionsRepository {
const HomeActionsRepository({required final DioHttpClient client})
: _client = client;

final DioHttpClient _client;

Future<Either<Exception, List<ActionItem>>> fetch({
required final ThemeType? themeType,
}) async {
final queryParameters = {'status': 'en_cours'};
if (themeType != null) {
queryParameters.putIfAbsent('thematique', () => themeType.name);
}
final response = await _client.get(
Uri(path: Endpoints.actions, queryParameters: queryParameters).toString(),
);

if (isResponseUnsuccessful(response.statusCode)) {
return Left(Exception('Erreur lors de la récupération des actions'));
}

final json = response.data! as List<dynamic>;

return Right(
json
.take(5)
.map(
(final e) => ActionItemMapper.fromJson(e as Map<String, dynamic>),
)
.toList(),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:app/features/actions/home/infrastructure/home_actions_repository.dart';
import 'package:app/features/actions/home/presentation/bloc/home_actions_event.dart';
import 'package:app/features/actions/home/presentation/bloc/home_actions_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class HomeActionsBloc extends Bloc<HomeActionsEvent, HomeActionsState> {
HomeActionsBloc({required final HomeActionsRepository repository})
: super(const HomeActionsInitial()) {
on<HomeActionsLoadRequested>((final event, final emit) async {
final result = await repository.fetch(themeType: event.themeType);
result.fold(
(final l) => emit(const HomeActionsLoadSuccess(actions: [])),
(final r) => emit(HomeActionsLoadSuccess(actions: r)),
);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'package:app/features/theme/core/domain/theme_type.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';

@immutable
sealed class HomeActionsEvent extends Equatable {
const HomeActionsEvent();

@override
List<Object?> get props => [];
}

@immutable
final class HomeActionsLoadRequested extends HomeActionsEvent {
const HomeActionsLoadRequested(this.themeType);

final ThemeType? themeType;

@override
List<Object?> get props => [themeType];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'package:app/features/actions/list/domain/action_item.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';

@immutable
sealed class HomeActionsState extends Equatable {
const HomeActionsState();

@override
List<Object> get props => [];
}

@immutable
final class HomeActionsInitial extends HomeActionsState {
const HomeActionsInitial();
}

@immutable
final class HomeActionsLoadSuccess extends HomeActionsState {
const HomeActionsLoadSuccess({required this.actions});

final List<ActionItem> actions;

@override
List<Object> get props => [actions];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import 'package:app/core/presentation/widgets/fondamentaux/shadows.dart';
import 'package:app/features/accueil/presentation/widgets/title_section.dart';
import 'package:app/features/actions/detail/presentation/pages/action_detail_page.dart';
import 'package:app/features/actions/home/presentation/bloc/home_actions_bloc.dart';
import 'package:app/features/actions/home/presentation/bloc/home_actions_event.dart';
import 'package:app/features/actions/home/presentation/bloc/home_actions_state.dart';
import 'package:app/features/actions/list/domain/action_item.dart';
import 'package:app/features/actions/list/presentation/pages/action_list_page.dart';
import 'package:app/features/theme/core/domain/theme_type.dart';
import 'package:app/features/theme/presentation/widgets/theme_type_tag.dart';
import 'package:app/l10n/l10n.dart';
import 'package:dsfr/dsfr.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';

class ActionsSection extends StatelessWidget {
const ActionsSection({super.key, this.themeType});

final ThemeType? themeType;

@override
Widget build(final BuildContext context) {
context.read<HomeActionsBloc>().add(HomeActionsLoadRequested(themeType));

return BlocBuilder<HomeActionsBloc, HomeActionsState>(
builder: (final context, final state) => switch (state) {
HomeActionsInitial() => const SizedBox.shrink(),
HomeActionsLoadSuccess() => _Section(state),
},
);
}
}

class _Section extends StatelessWidget {
const _Section(this.state);

final HomeActionsLoadSuccess state;

@override
Widget build(final context) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const TitleSection(
title: Localisation.homeActionsTitle,
subTitle: Localisation.homeActionsSubTitle,
),
const SizedBox(height: DsfrSpacings.s2w),
_Actions(actions: state.actions),
const SizedBox(height: DsfrSpacings.s2w),
Align(
alignment: Alignment.centerLeft,
child: DsfrLink.md(
label: Localisation.homeActionsLink,
onTap: () async =>
GoRouter.of(context).pushNamed(ActionListPage.name),
),
),
],
);
}

class _Actions extends StatelessWidget {
const _Actions({required this.actions});

final List<ActionItem> actions;

@override
Widget build(final BuildContext context) => actions.isEmpty
? const Text(
Localisation.homeActionsListEmpty,
style: DsfrTextStyle.bodySm(),
)
: SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
clipBehavior: Clip.none,
child: IntrinsicHeight(
child: Row(
children: actions
.map(_Action.new)
.separator(const SizedBox(width: DsfrSpacings.s2w))
.toList(),
),
),
);
}

class _Action extends StatelessWidget {
const _Action(this.item);

final ActionItem item;

@override
Widget build(final context) {
const width = 250.0;

return GestureDetector(
onTap: () async {
final result = await GoRouter.of(context).pushNamed(
ActionDetailPage.name,
pathParameters: {'id': item.id.value},
);

if (result != true || !context.mounted) {}
// if (context.mounted) {
// context.read<MissionHomeBloc>().add(const MissionHomeFetch());
// }
},
child: DecoratedBox(
decoration: const ShapeDecoration(
color: Colors.white,
shadows: recommandationOmbre,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(DsfrSpacings.s1w)),
),
),
child: SizedBox(
width: width,
child: Padding(
padding: const EdgeInsets.all(DsfrSpacings.s2w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ThemeTypeTag(themeType: item.themeType),
const SizedBox(height: DsfrSpacings.s1w),
Expanded(
child: Text(item.titre, style: const DsfrTextStyle.bodyLg()),
),
],
),
),
),
),
);
}
}
4 changes: 1 addition & 3 deletions app/lib/features/actions/list/application/fetch_actions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ class FetchActions {
final result = await _port.fetchActions();

return result.map(
(final actions) => actions
.where((final action) => action.status != ActionStatus.toDo)
.sorted((final a, final b) {
(final actions) => actions.sorted((final a, final b) {
final order = [
ActionStatus.inProgress,
ActionStatus.done,
Expand Down
5 changes: 4 additions & 1 deletion app/lib/features/actions/list/domain/action_item.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import 'package:app/features/actions/core/domain/action_id.dart';
import 'package:app/features/actions/core/domain/action_status.dart';
import 'package:app/features/theme/core/domain/theme_type.dart';
import 'package:equatable/equatable.dart';

final class ActionItem extends Equatable {
const ActionItem({
required this.id,
required this.themeType,
required this.titre,
required this.status,
});

final ActionId id;
final ThemeType themeType;
final String titre;
final ActionStatus status;

@override
List<Object?> get props => [id, titre, status];
List<Object?> get props => [id, themeType, titre, status];
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import 'package:app/features/actions/core/domain/action_id.dart';
import 'package:app/features/actions/core/domain/action_status.dart';
import 'package:app/features/actions/list/domain/action_item.dart';
import 'package:app/features/theme/core/domain/theme_type.dart';

abstract final class ActionItemMapper {
const ActionItemMapper._();

static ActionItem fromJson(final Map<String, dynamic> json) => ActionItem(
id: ActionId(json['id'] as String),
themeType: _mapThemeType(json['thematique'] as String),
titre: json['titre'] as String,
status: _actionStatusfromJson(json['status'] as String),
);
Expand All @@ -22,4 +24,12 @@ abstract final class ActionItemMapper {
// ignore: no-equal-switch-expression-cases
_ => ActionStatus.toDo,
};

static ThemeType _mapThemeType(final String? type) => switch (type) {
'alimentation' => ThemeType.alimentation,
'transport' => ThemeType.transport,
'consommation' => ThemeType.consommation,
'logement' => ThemeType.logement,
_ => ThemeType.decouverte,
};
}
Loading

0 comments on commit 786c836

Please sign in to comment.