@pi-spi/app 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/lib/module/core/api.js +14 -5
  2. package/lib/module/core/api.js.map +1 -1
  3. package/lib/module/l10n/app_localizations.js.map +1 -1
  4. package/lib/module/l10n/app_localizations_en.js +8 -0
  5. package/lib/module/l10n/app_localizations_en.js.map +1 -1
  6. package/lib/module/l10n/app_localizations_fr.js +8 -0
  7. package/lib/module/l10n/app_localizations_fr.js.map +1 -1
  8. package/lib/module/l10n/app_localizations_pt.js +8 -0
  9. package/lib/module/l10n/app_localizations_pt.js.map +1 -1
  10. package/lib/module/modules/categorie/presentation/pages/categorie_edit_icon_btn.js +25 -35
  11. package/lib/module/modules/categorie/presentation/pages/categorie_edit_icon_btn.js.map +1 -1
  12. package/lib/module/modules/profile/presentation/pages/profile_page_avatar.js +6 -28
  13. package/lib/module/modules/profile/presentation/pages/profile_page_avatar.js.map +1 -1
  14. package/lib/module/modules/profile/presentation/pages/profile_page_contacts.js +0 -1
  15. package/lib/module/modules/profile/presentation/pages/profile_page_contacts.js.map +1 -1
  16. package/lib/module/modules/profile/presentation/pages/profile_page_contacts_actions.js +122 -162
  17. package/lib/module/modules/profile/presentation/pages/profile_page_contacts_actions.js.map +1 -1
  18. package/lib/module/modules/qrcode/presentation/pages/qrcode_alias_page.js +0 -1
  19. package/lib/module/modules/qrcode/presentation/pages/qrcode_alias_page.js.map +1 -1
  20. package/lib/module/modules/qrcode/presentation/pages/qrcode_alias_page_share.js +26 -38
  21. package/lib/module/modules/qrcode/presentation/pages/qrcode_alias_page_share.js.map +1 -1
  22. package/lib/module/modules/subscription/presentation/pages/subscription_details/subscription_details_page.js +0 -1
  23. package/lib/module/modules/subscription/presentation/pages/subscription_details/subscription_details_page.js.map +1 -1
  24. package/lib/module/modules/subscription/presentation/pages/subscription_details/subscription_details_page_actions.js +0 -1
  25. package/lib/module/modules/subscription/presentation/pages/subscription_details/subscription_details_page_actions.js.map +1 -1
  26. package/lib/module/modules/subscription/presentation/pages/subscription_details/subscription_details_page_note_sheet.js +38 -93
  27. package/lib/module/modules/subscription/presentation/pages/subscription_details/subscription_details_page_note_sheet.js.map +1 -1
  28. package/lib/module/modules/subscription/presentation/pages/subscription_list/subscription_list_empty_widget.js +0 -1
  29. package/lib/module/modules/subscription/presentation/pages/subscription_list/subscription_list_empty_widget.js.map +1 -1
  30. package/lib/module/modules/subscription/presentation/pages/subscription_list/subscription_list_widget.js +0 -1
  31. package/lib/module/modules/subscription/presentation/pages/subscription_list/subscription_list_widget.js.map +1 -1
  32. package/lib/module/modules/subscription/presentation/pages/subscription_menu_widget.js +23 -42
  33. package/lib/module/modules/subscription/presentation/pages/subscription_menu_widget.js.map +1 -1
  34. package/lib/module/modules/transactions/infra/transaction_output_remote.js +2 -1
  35. package/lib/module/modules/transactions/infra/transaction_output_remote.js.map +1 -1
  36. package/lib/module/modules/transactions/presentation/pages/transaction_details/transaction_details_page_actions.js +0 -1
  37. package/lib/module/modules/transactions/presentation/pages/transaction_details/transaction_details_page_actions.js.map +1 -1
  38. package/lib/module/modules/transactions/presentation/pages/transaction_details/transaction_details_page_actions_cancel.js +34 -61
  39. package/lib/module/modules/transactions/presentation/pages/transaction_details/transaction_details_page_actions_cancel.js.map +1 -1
  40. package/lib/module/modules/transactions/presentation/pages/transaction_details/transaction_details_page_actions_return.js +49 -62
  41. package/lib/module/modules/transactions/presentation/pages/transaction_details/transaction_details_page_actions_return.js.map +1 -1
  42. package/lib/module/modules/transactions/presentation/pages/transaction_details/transaction_details_page_ticket.js +68 -88
  43. package/lib/module/modules/transactions/presentation/pages/transaction_details/transaction_details_page_ticket.js.map +1 -1
  44. package/lib/module/modules/transactions/presentation/pages/transaction_form/transaction_form_input_pays.js +77 -0
  45. package/lib/module/modules/transactions/presentation/pages/transaction_form/transaction_form_input_pays.js.map +1 -0
  46. package/lib/module/modules/transactions/presentation/pages/transaction_form/transaction_form_input_psp.js +49 -155
  47. package/lib/module/modules/transactions/presentation/pages/transaction_form/transaction_form_input_psp.js.map +1 -1
  48. package/lib/module/modules/transactions/presentation/pages/transaction_form/transaction_form_page_othr.js +8 -28
  49. package/lib/module/modules/transactions/presentation/pages/transaction_form/transaction_form_page_othr.js.map +1 -1
  50. package/lib/module/modules/transactions/presentation/pages/transaction_form/transaction_form_page_verification.js +0 -1
  51. package/lib/module/modules/transactions/presentation/pages/transaction_form/transaction_form_page_verification.js.map +1 -1
  52. package/lib/module/modules/transactions/presentation/pages/transaction_recents/transaction_recents_nombre_widget.js +50 -42
  53. package/lib/module/modules/transactions/presentation/pages/transaction_recents/transaction_recents_nombre_widget.js.map +1 -1
  54. package/lib/module/modules/transactions/presentation/pages/transaction_recents/transaction_recents_widget.js +0 -1
  55. package/lib/module/modules/transactions/presentation/pages/transaction_recents/transaction_recents_widget.js.map +1 -1
  56. package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.js +0 -1
  57. package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.js.map +1 -1
  58. package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions_reject.js +37 -67
  59. package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions_reject.js.map +1 -1
  60. package/lib/module/shared/widgets/confirm_action_sheet_widget.js +34 -67
  61. package/lib/module/shared/widgets/confirm_action_sheet_widget.js.map +1 -1
  62. package/lib/module/shared/widgets/image_select_widget.js +43 -107
  63. package/lib/module/shared/widgets/image_select_widget.js.map +1 -1
  64. package/lib/module/shared/widgets/notification_dialog.js +25 -35
  65. package/lib/module/shared/widgets/notification_dialog.js.map +1 -1
  66. package/lib/module/shared/widgets/selection.js +381 -5
  67. package/lib/module/shared/widgets/selection.js.map +1 -1
  68. package/lib/typescript/src/core/api.d.ts +6 -0
  69. package/lib/typescript/src/core/api.d.ts.map +1 -1
  70. package/lib/typescript/src/l10n/app_localizations.d.ts +16 -0
  71. package/lib/typescript/src/l10n/app_localizations.d.ts.map +1 -1
  72. package/lib/typescript/src/l10n/app_localizations_en.d.ts.map +1 -1
  73. package/lib/typescript/src/l10n/app_localizations_fr.d.ts.map +1 -1
  74. package/lib/typescript/src/l10n/app_localizations_pt.d.ts.map +1 -1
  75. package/lib/typescript/src/modules/categorie/presentation/pages/categorie_edit_icon_btn.d.ts.map +1 -1
  76. package/lib/typescript/src/modules/profile/presentation/pages/profile_page_avatar.d.ts.map +1 -1
  77. package/lib/typescript/src/modules/profile/presentation/pages/profile_page_contacts.d.ts.map +1 -1
  78. package/lib/typescript/src/modules/profile/presentation/pages/profile_page_contacts_actions.d.ts.map +1 -1
  79. package/lib/typescript/src/modules/qrcode/presentation/pages/qrcode_alias_page.d.ts.map +1 -1
  80. package/lib/typescript/src/modules/qrcode/presentation/pages/qrcode_alias_page_share.d.ts.map +1 -1
  81. package/lib/typescript/src/modules/subscription/presentation/pages/subscription_details/subscription_details_page.d.ts.map +1 -1
  82. package/lib/typescript/src/modules/subscription/presentation/pages/subscription_details/subscription_details_page_actions.d.ts.map +1 -1
  83. package/lib/typescript/src/modules/subscription/presentation/pages/subscription_details/subscription_details_page_note_sheet.d.ts +2 -2
  84. package/lib/typescript/src/modules/subscription/presentation/pages/subscription_details/subscription_details_page_note_sheet.d.ts.map +1 -1
  85. package/lib/typescript/src/modules/subscription/presentation/pages/subscription_menu_widget.d.ts.map +1 -1
  86. package/lib/typescript/src/modules/transactions/infra/transaction_output_remote.d.ts.map +1 -1
  87. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_details/transaction_details_page_actions.d.ts.map +1 -1
  88. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_details/transaction_details_page_actions_cancel.d.ts.map +1 -1
  89. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_details/transaction_details_page_actions_return.d.ts.map +1 -1
  90. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_form/transaction_form_input_pays.d.ts +17 -0
  91. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_form/transaction_form_input_pays.d.ts.map +1 -0
  92. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_form/transaction_form_input_psp.d.ts +6 -2
  93. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_form/transaction_form_input_psp.d.ts.map +1 -1
  94. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_form/transaction_form_page_othr.d.ts.map +1 -1
  95. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_form/transaction_form_page_verification.d.ts.map +1 -1
  96. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_recents/transaction_recents_nombre_widget.d.ts.map +1 -1
  97. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.d.ts.map +1 -1
  98. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions_reject.d.ts.map +1 -1
  99. package/lib/typescript/src/shared/widgets/confirm_action_sheet_widget.d.ts +4 -1
  100. package/lib/typescript/src/shared/widgets/confirm_action_sheet_widget.d.ts.map +1 -1
  101. package/lib/typescript/src/shared/widgets/image_select_widget.d.ts +6 -4
  102. package/lib/typescript/src/shared/widgets/image_select_widget.d.ts.map +1 -1
  103. package/lib/typescript/src/shared/widgets/notification_dialog.d.ts +2 -2
  104. package/lib/typescript/src/shared/widgets/notification_dialog.d.ts.map +1 -1
  105. package/lib/typescript/src/shared/widgets/selection.d.ts +69 -2
  106. package/lib/typescript/src/shared/widgets/selection.d.ts.map +1 -1
  107. package/package.json +1 -1
  108. package/src/core/api.ts +11 -2
  109. package/src/l10n/app_localizations.ts +16 -0
  110. package/src/l10n/app_localizations_en.ts +8 -0
  111. package/src/l10n/app_localizations_fr.ts +8 -0
  112. package/src/l10n/app_localizations_pt.ts +8 -0
  113. package/src/modules/categorie/presentation/pages/categorie_edit_icon_btn.tsx +25 -27
  114. package/src/modules/profile/presentation/pages/profile_page_avatar.tsx +6 -12
  115. package/src/modules/profile/presentation/pages/profile_page_contacts.tsx +0 -1
  116. package/src/modules/profile/presentation/pages/profile_page_contacts_actions.tsx +111 -117
  117. package/src/modules/qrcode/presentation/pages/qrcode_alias_page.tsx +0 -1
  118. package/src/modules/qrcode/presentation/pages/qrcode_alias_page_share.tsx +32 -40
  119. package/src/modules/subscription/presentation/pages/subscription_details/subscription_details_page.tsx +0 -1
  120. package/src/modules/subscription/presentation/pages/subscription_details/subscription_details_page_actions.tsx +0 -1
  121. package/src/modules/subscription/presentation/pages/subscription_details/subscription_details_page_note_sheet.tsx +20 -59
  122. package/src/modules/subscription/presentation/pages/subscription_list/subscription_list_empty_widget.tsx +1 -1
  123. package/src/modules/subscription/presentation/pages/subscription_list/subscription_list_widget.tsx +1 -1
  124. package/src/modules/subscription/presentation/pages/subscription_menu_widget.tsx +25 -39
  125. package/src/modules/transactions/infra/transaction_output_remote.ts +6 -1
  126. package/src/modules/transactions/presentation/pages/transaction_details/transaction_details_page_actions.tsx +0 -1
  127. package/src/modules/transactions/presentation/pages/transaction_details/transaction_details_page_actions_cancel.tsx +39 -41
  128. package/src/modules/transactions/presentation/pages/transaction_details/transaction_details_page_actions_return.tsx +41 -46
  129. package/src/modules/transactions/presentation/pages/transaction_details/transaction_details_page_ticket.tsx +46 -40
  130. package/src/modules/transactions/presentation/pages/transaction_form/transaction_form_input_pays.tsx +92 -0
  131. package/src/modules/transactions/presentation/pages/transaction_form/transaction_form_input_psp.tsx +48 -94
  132. package/src/modules/transactions/presentation/pages/transaction_form/transaction_form_page_othr.tsx +4 -17
  133. package/src/modules/transactions/presentation/pages/transaction_form/transaction_form_page_verification.tsx +0 -1
  134. package/src/modules/transactions/presentation/pages/transaction_recents/transaction_recents_nombre_widget.tsx +49 -42
  135. package/src/modules/transactions/presentation/pages/transaction_recents/transaction_recents_widget.tsx +1 -1
  136. package/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.tsx +0 -1
  137. package/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions_reject.tsx +47 -48
  138. package/src/shared/widgets/confirm_action_sheet_widget.tsx +30 -34
  139. package/src/shared/widgets/image_select_widget.tsx +46 -61
  140. package/src/shared/widgets/notification_dialog.tsx +66 -70
  141. package/src/shared/widgets/selection.tsx +292 -4
  142. package/lib/module/modules/transactions/presentation/pages/transaction_details/sheet_box.js +0 -47
  143. package/lib/module/modules/transactions/presentation/pages/transaction_details/sheet_box.js.map +0 -1
  144. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_details/sheet_box.d.ts +0 -10
  145. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_details/sheet_box.d.ts.map +0 -1
  146. package/src/modules/transactions/presentation/pages/transaction_details/sheet_box.tsx +0 -30
@@ -10,10 +10,10 @@ import { AppRouter, useAppRouter, type SdkRouter } from '../../../../../core/rou
10
10
  import { Themer, useTheme } from '../../../../../core/theme';
11
11
  import type { AppLocalizations } from '../../../../../l10n/app_localizations';
12
12
  import { useAppLocalizations } from '../../../../../l10n/localizations';
13
- import { TextButtonIcon } from '../../../../../shared/material/buttons';
14
- import { Card } from '../../../../../shared/material/card';
13
+ import { ElevatedButton, TextButtonIcon } from '../../../../../shared/material/buttons';
15
14
  import { showModalBottomSheet } from '../../../../../shared/material/overlays';
16
15
  import { ImageSelectWidget } from '../../../../../shared/widgets/image_select_widget';
16
+ import { SelectionBoutons, SelectionSheet } from '../../../../../shared/widgets/selection';
17
17
  import type { Transaction } from '../../../domain/models/transaction';
18
18
 
19
19
  const logger = new Logger();
@@ -108,7 +108,7 @@ export function TransactionDetailsPageTicket({ transaction }: { transaction: Tra
108
108
  );
109
109
  }
110
110
 
111
- /** Feuille pour visualiser le ticket de caisse */
111
+ /** Feuille pour visualiser le ticket de caisse : l'image en grand, puis Supprimer et Partager. */
112
112
  function TicketVisualiser({
113
113
  uri,
114
114
  transaction,
@@ -124,37 +124,48 @@ function TicketVisualiser({
124
124
  const { height } = useWindowDimensions();
125
125
 
126
126
  return (
127
- <View style={[styles.sheet, { backgroundColor: theme.colorScheme.surface, minHeight: height * 0.2, height: height * 0.8 }]}>
128
- {/* Bouton partager */}
129
- <View style={styles.actions}>
130
- <Pressable accessibilityRole="button" hitSlop={8} style={styles.iconButton} onPress={() => void shareTicket(transaction, traductions)}>
131
- <MaterialIcons name={Platform.OS === 'ios' ? 'ios-share' : 'share'} size={24} color={theme.colorScheme.onSurface} />
132
- </Pressable>
133
- </View>
134
- <View style={styles.gap20} />
135
- {/* Image du ticket de caisse */}
136
- <Card style={styles.flex}>
137
- <View style={styles.card}>
138
- <View style={styles.gap20} />
139
- {/* Titre */}
140
- <Text style={[theme.textTheme.headlineLarge, styles.center]}>{traductions.transactionDetailsTicketCaisse}</Text>
141
- <View style={styles.gap8} />
142
- {/* Sous-titre */}
143
- <Text style={[theme.textTheme.displaySmall, styles.center]}>{traductions.transactionDetailsTicketCaisseSubtitle}</Text>
144
- <View style={styles.image}>
127
+ <SelectionSheet
128
+ hauteurMax={0.85}
129
+ titre={traductions.transactionDetailsTicketCaisse}
130
+ entete={
131
+ <Text style={[theme.textTheme.displaySmall, { color: Themer.neural03Color }]}>
132
+ {traductions.transactionDetailsTicketCaisseSubtitle}
133
+ </Text>
134
+ }
135
+ contenu={
136
+ <>
137
+ {/* Image du ticket de caisse */}
138
+ <View style={[styles.image, { height: height * 0.5 }]}>
145
139
  <Image source={{ uri }} style={styles.flex} contentFit="contain" />
146
140
  </View>
147
- </View>
148
- </Card>
149
- {/* Bouton supprimer */}
150
- <View style={styles.gap20} />
151
- <TextButtonIcon
152
- onPress={onDelete}
153
- icon={<MaterialIcons name="delete-forever" size={24} color={Themer.systemErrorColor} />}
154
- label={traductions.btnTextDelete}
155
- textStyle={[theme.textTheme.bodySmall, { color: Themer.systemErrorColor }]}
156
- />
157
- </View>
141
+ <SelectionBoutons
142
+ boutons={[
143
+ // Supprimer
144
+ <TextButtonIcon
145
+ key="supprimer"
146
+ onPress={onDelete}
147
+ icon={<MaterialIcons name="delete-outline" size={22} color={Themer.systemErrorColor} />}
148
+ label={traductions.btnTextDelete}
149
+ textStyle={{ color: Themer.systemErrorColor }}
150
+ />,
151
+ // Partager
152
+ <ElevatedButton key="partager" onPress={() => void shareTicket(transaction, traductions)}>
153
+ <View style={styles.row}>
154
+ <MaterialIcons
155
+ name={Platform.OS === 'ios' ? 'ios-share' : 'share'}
156
+ size={18}
157
+ color={theme.elevatedButtonTheme.foregroundColor}
158
+ />
159
+ <Text style={[theme.elevatedButtonTheme.textStyle, styles.libelle, { color: theme.elevatedButtonTheme.foregroundColor }]}>
160
+ {traductions.transactionDetailsTicketPartager}
161
+ </Text>
162
+ </View>
163
+ </ElevatedButton>,
164
+ ]}
165
+ />
166
+ </>
167
+ }
168
+ />
158
169
  );
159
170
  }
160
171
 
@@ -175,12 +186,7 @@ async function shareTicket(transaction: Transaction, traductions: AppLocalizatio
175
186
  const styles = StyleSheet.create({
176
187
  flex: { flex: 1 },
177
188
  thumbnail: { width: 30, height: 30, borderRadius: 8 },
178
- sheet: { borderTopLeftRadius: 20, borderTopRightRadius: 20, padding: 10 },
179
- actions: { flexDirection: 'row', justifyContent: 'space-between' },
180
- iconButton: { padding: 8 },
181
- card: { flex: 1, padding: 20 },
182
- image: { flex: 1, margin: 20, borderRadius: 10, overflow: 'hidden' },
183
- center: { textAlign: 'center' },
184
- gap8: { height: 8 },
185
- gap20: { height: 20 },
189
+ image: { marginHorizontal: 20, marginTop: 8, marginBottom: 16, borderRadius: 14, overflow: 'hidden' },
190
+ row: { flexDirection: 'row', alignItems: 'center' },
191
+ libelle: { marginLeft: 8 },
186
192
  });
@@ -0,0 +1,92 @@
1
+ import { useState } from 'react';
2
+
3
+ import { AppRouter, useAppRouter } from '../../../../../core/router';
4
+ import { useAppLocalizations } from '../../../../../l10n/localizations';
5
+ import { showModalBottomSheet } from '../../../../../shared/material/overlays';
6
+ import { UEMOACountry } from '../../../../../shared/models/uemoa_countries';
7
+ import {
8
+ SbSelectionLigne,
9
+ SbSelectionListe,
10
+ SbSelectionPastille,
11
+ SbSelectionRecherche,
12
+ selectionContient,
13
+ SelectionChamp,
14
+ SelectionSheet,
15
+ } from '../../../../../shared/widgets/selection';
16
+ import type { TransactionSendCommand } from '../../../domain/models/transaction_send/transaction_send_command';
17
+ import type { TransactionSendBloc } from '../../bloc/transaction_send/transaction_send_bloc';
18
+ import { TransactionSendFormChangedEvent } from '../../bloc/transaction_send/transaction_send_event';
19
+
20
+ /**
21
+ * Pays de l'institution financière : champ qui ouvre une feuille de
22
+ * sélection, avec recherche par nom ou par code. Changer de pays efface
23
+ * l'institution choisie.
24
+ */
25
+ export function TransactionSendFormInputPays({
26
+ command,
27
+ transactionSendBloc,
28
+ }: {
29
+ command: TransactionSendCommand;
30
+ transactionSendBloc: TransactionSendBloc;
31
+ }) {
32
+ const traductions = useAppLocalizations();
33
+ const router = useAppRouter();
34
+ const selected = command.pspPays != null ? UEMOACountry.get(command.pspPays) : undefined;
35
+
36
+ const select = async () => {
37
+ const value = await showModalBottomSheet<UEMOACountry>(router, {
38
+ builder: () => <SelectPays selected={selected} />,
39
+ });
40
+ if (value != null && value.iso !== command.pspPays) {
41
+ command.pspPays = value.iso;
42
+ command.pspCode = undefined;
43
+ command.pspNom = undefined;
44
+ transactionSendBloc.add(new TransactionSendFormChangedEvent(command));
45
+ }
46
+ };
47
+
48
+ return (
49
+ <SelectionChamp
50
+ libelle={traductions.transactionFormOthrPaysLabel}
51
+ valeur={selected?.name}
52
+ pastille={selected != null ? <SbSelectionPastille code={selected.iso3} /> : undefined}
53
+ onPress={() => void select()}
54
+ />
55
+ );
56
+ }
57
+
58
+ /** Feuille de sélection d'un pays de l'UEMOA ; renvoie le pays choisi. */
59
+ export function SelectPays({ selected }: { selected?: UEMOACountry }) {
60
+ const traductions = useAppLocalizations();
61
+ const router = useAppRouter();
62
+ const [recherche, setRecherche] = useState('');
63
+
64
+ const pays =
65
+ recherche.trim() === ''
66
+ ? UEMOACountry.liste
67
+ : UEMOACountry.liste.filter(
68
+ (p) =>
69
+ selectionContient(p.name, recherche) || selectionContient(p.iso, recherche) || selectionContient(p.iso3, recherche),
70
+ );
71
+
72
+ return (
73
+ <SelectionSheet
74
+ titre={traductions.transactionFormOthrPaysLabel}
75
+ entete={<SbSelectionRecherche hintText={traductions.transactionFormOthrPaysRecherche} onChanged={setRecherche} />}
76
+ contenu={
77
+ <SbSelectionListe
78
+ vide={traductions.transactionFormOthrPaysAucun}
79
+ lignes={pays.map((p) => (
80
+ <SbSelectionLigne
81
+ key={p.iso}
82
+ pastille={<SbSelectionPastille code={p.iso3} choisie={p.iso === selected?.iso} />}
83
+ texte={p.name}
84
+ choisie={p.iso === selected?.iso}
85
+ onPress={() => AppRouter.pop(router, { value: p })}
86
+ />
87
+ ))}
88
+ />
89
+ }
90
+ />
91
+ );
92
+ }
@@ -1,20 +1,24 @@
1
- import { MaterialIcons } from '@expo/vector-icons';
2
- import { useEffect, useRef, useState } from 'react';
3
- import { Animated, Easing, Keyboard, Pressable, ScrollView, StyleSheet, Text, TextInput, useWindowDimensions, View } from 'react-native';
4
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
1
+ import { useState } from 'react';
2
+ import { View, StyleSheet } from 'react-native';
5
3
 
6
4
  import { AppRouter, useAppRouter } from '../../../../../core/router';
7
- import { Themer, useTheme } from '../../../../../core/theme';
8
5
  import { useAppLocalizations } from '../../../../../l10n/localizations';
9
- import { Card } from '../../../../../shared/material/card';
10
6
  import { showModalBottomSheet } from '../../../../../shared/material/overlays';
11
- import { CustomTextInput } from '../../../../../shared/widgets/input_text';
7
+ import {
8
+ SbSelectionLigne,
9
+ SbSelectionListe,
10
+ SbSelectionPastille,
11
+ SbSelectionRecherche,
12
+ selectionContient,
13
+ SelectionChamp,
14
+ SelectionSheet,
15
+ } from '../../../../../shared/widgets/selection';
12
16
  import type { Participant } from '../../../domain/models/participant/participant';
13
17
  import type { TransactionSendCommand } from '../../../domain/models/transaction_send/transaction_send_command';
14
18
  import type { TransactionSendBloc } from '../../bloc/transaction_send/transaction_send_bloc';
15
19
  import { TransactionSendFormChangedEvent } from '../../bloc/transaction_send/transaction_send_event';
16
20
 
17
- /** Champ « Institution » : ouvre la liste des participants du pays choisi. */
21
+ /** Institution financière du bénéficiaire : champ qui ouvre la liste des participants du pays choisi, avec recherche. */
18
22
  export function TransactionSendFormInputPsp({
19
23
  command,
20
24
  transactionSendBloc,
@@ -25,14 +29,11 @@ export function TransactionSendFormInputPsp({
25
29
  participants: Participant[];
26
30
  }) {
27
31
  const traductions = useAppLocalizations();
28
- const theme = useTheme();
29
32
  const router = useAppRouter();
30
33
  const psp = participants.find((p) => p.codeMembre === command.pspCode) ?? null;
31
34
 
32
35
  const select = async () => {
33
- Keyboard.dismiss();
34
36
  const value = await showModalBottomSheet<Participant>(router, {
35
- themed: true,
36
37
  builder: () => <SelectPsp participants={participants} pays={command.pspPays} selected={psp} />,
37
38
  });
38
39
  if (value != null) {
@@ -43,21 +44,20 @@ export function TransactionSendFormInputPsp({
43
44
  };
44
45
 
45
46
  return (
46
- <Pressable onPress={() => void select()}>
47
- <View pointerEvents="none">
48
- <CustomTextInput
49
- labelText={traductions.transactionFormOthrNomLabel}
50
- hintText={traductions.transactionFormOthrNomLabel}
51
- value={psp?.nomMembre ?? ''}
52
- readOnly
53
- suffixIcon={<MaterialIcons name="keyboard-arrow-down" size={24} color={theme.colorScheme.onSurface} />}
54
- />
55
- </View>
56
- </Pressable>
47
+ <SelectionChamp
48
+ libelle={traductions.transactionFormOthrNomLabel}
49
+ valeur={psp?.nomMembre}
50
+ pastille={psp != null ? <SbSelectionPastille icone="account-balance" /> : undefined}
51
+ onPress={() => void select()}
52
+ />
57
53
  );
58
54
  }
59
55
 
60
- /** Feuille de choix d'un participant, avec recherche ; les participants indisponibles sont grisés. */
56
+ /**
57
+ * Feuille de choix d'un participant, avec recherche ; renvoie le
58
+ * participant choisi. Les participants indisponibles sont grisés et ne
59
+ * peuvent pas être choisis, sauf avec [canSelectDisablePsp].
60
+ */
61
61
  export function SelectPsp({
62
62
  canSelectDisablePsp = false,
63
63
  participants,
@@ -70,91 +70,45 @@ export function SelectPsp({
70
70
  selected?: Participant | null;
71
71
  }) {
72
72
  const traductions = useAppLocalizations();
73
- const theme = useTheme();
74
73
  const router = useAppRouter();
75
- const insets = useSafeAreaInsets();
76
- const { height } = useWindowDimensions();
77
- const [keyword, setKeyword] = useState('');
78
- const [searchFocused, setSearchFocused] = useState(false);
79
- // Comme l'AnimatedContainer Flutter : 50 % de l'écran, 80 % pendant la recherche
80
- const sheetHeight = useRef(new Animated.Value(height * 0.5)).current;
81
- useEffect(() => {
82
- Animated.timing(sheetHeight, {
83
- toValue: height * (searchFocused ? 0.8 : 0.5),
84
- duration: 300,
85
- easing: Easing.inOut(Easing.ease),
86
- useNativeDriver: false,
87
- }).start();
88
- }, [height, searchFocused, sheetHeight]);
74
+ const [recherche, setRecherche] = useState('');
89
75
 
90
76
  const psps = participants.filter(
91
77
  (p) =>
92
78
  (pays == null || p.pays.toLowerCase() === pays.toLowerCase()) &&
93
- (keyword === '' || p.nomMembre.toLowerCase().includes(keyword.toLowerCase())),
79
+ (recherche.trim() === '' || selectionContient(p.nomMembre, recherche)),
94
80
  );
95
81
 
96
82
  return (
97
- <Animated.View style={[styles.sheet, { backgroundColor: theme.colorScheme.surface, height: sheetHeight, paddingBottom: insets.bottom }]}>
98
- <Text style={theme.textTheme.headlineLarge}>{traductions.transactionFormOthrNomLabel}</Text>
99
- <View style={styles.gap10} />
100
- {/* Recherche */}
101
- <Card>
102
- <View style={[styles.search, { backgroundColor: theme.inputDecorationTheme.fillColor }]}>
103
- <MaterialIcons name="search" size={24} color={Themer.neural03Color} />
104
- <TextInput
105
- style={[theme.textTheme.displayLarge, styles.searchInput]}
106
- placeholder={traductions.btnTextRechercher}
107
- placeholderTextColor={Themer.neural03Color}
108
- onFocus={() => setSearchFocused(true)}
109
- onBlur={() => setSearchFocused(false)}
110
- onChangeText={setKeyword}
111
- />
112
- </View>
113
- </Card>
114
- <View style={styles.gap20} />
115
- {/* Liste des participants */}
116
- <Card style={styles.flex}>
117
- <ScrollView keyboardShouldPersistTaps="handled">
118
- {psps.map((psp) => {
119
- const disabled = !canSelectDisablePsp && psp.isDisable();
120
- const isCurrent = psp.codeMembre === selected?.codeMembre;
83
+ <SelectionSheet
84
+ titre={traductions.transactionFormOthrNomLabel}
85
+ entete={<SbSelectionRecherche hintText={traductions.transactionFormOthrPspRecherche} onChanged={setRecherche} />}
86
+ contenu={
87
+ <SbSelectionListe
88
+ vide={traductions.transactionFormOthrPspAucune}
89
+ lignes={psps.map((psp) => {
90
+ const choisie = psp.codeMembre === selected?.codeMembre;
91
+ const indisponible = !canSelectDisablePsp && psp.isDisable();
121
92
  return (
122
- <View key={psp.codeMembre} style={styles.itemPadding}>
123
- <Pressable
124
- disabled={disabled}
125
- onPress={() => AppRouter.pop(router, { value: psp })}
126
- style={[styles.item, isCurrent ? { backgroundColor: Themer.neural01Color } : null]}
127
- >
128
- <View style={styles.flex}>
129
- <Text
130
- numberOfLines={1}
131
- style={[theme.textTheme.headlineSmall, styles.pspName, disabled ? { color: Themer.neural02Color } : null]}
132
- >
133
- {psp.nomMembre}
134
- </Text>
135
- {disabled && (
136
- <Text style={[theme.textTheme.displaySmall, { color: Themer.neural02Color }]}>{traductions.indisponible}</Text>
137
- )}
138
- </View>
139
- {isCurrent && <MaterialIcons name="check" size={24} color={Themer.neural03Color} />}
140
- </Pressable>
93
+ <View key={psp.codeMembre} style={indisponible ? styles.indisponible : null}>
94
+ <SbSelectionLigne
95
+ pastille={<SbSelectionPastille icone="account-balance" choisie={choisie} />}
96
+ texte={psp.nomMembre}
97
+ detail={indisponible ? traductions.indisponible : undefined}
98
+ choisie={choisie}
99
+ onPress={() => {
100
+ if (!indisponible) AppRouter.pop(router, { value: psp });
101
+ }}
102
+ />
141
103
  </View>
142
104
  );
143
105
  })}
144
- </ScrollView>
145
- </Card>
146
- </Animated.View>
106
+ />
107
+ }
108
+ />
147
109
  );
148
110
  }
149
111
 
150
112
  const styles = StyleSheet.create({
151
- flex: { flex: 1 },
152
- sheet: { borderRadius: 20, paddingTop: 20, paddingHorizontal: 20 },
153
- search: { height: 36, flexDirection: 'row', alignItems: 'center', paddingHorizontal: 12 },
154
- searchInput: { flex: 1, marginLeft: 8, paddingVertical: 0 },
155
- itemPadding: { paddingHorizontal: 8 },
156
- item: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 10, minHeight: 56, borderRadius: 20 },
157
- pspName: { fontWeight: '700', fontSize: 15 },
158
- gap10: { height: 10 },
159
- gap20: { height: 20 },
113
+ indisponible: { opacity: 0.5 },
160
114
  });
@@ -6,7 +6,6 @@ import { useBloc, useBlocBuilder } from '../../../../../core/bloc_react';
6
6
  import { useTheme } from '../../../../../core/theme';
7
7
  import type { AppLocalizations } from '../../../../../l10n/app_localizations';
8
8
  import { useAppLocalizations } from '../../../../../l10n/localizations';
9
- import { DropdownButton } from '../../../../../shared/material/dropdown';
10
9
  import { CompteValidator } from '../../../../../shared/models/compte_validator';
11
10
  import { UEMOACountry } from '../../../../../shared/models/uemoa_countries';
12
11
  import { useKeyboardVisible } from '../../../../../shared/utils/use_keyboard_visible';
@@ -17,11 +16,11 @@ import type { Participant } from '../../../domain/models/participant/participant
17
16
  import type { TransactionSendCommand } from '../../../domain/models/transaction_send/transaction_send_command';
18
17
  import { TransactionSendMethod } from '../../../domain/models/transaction_send/transaction_send_method';
19
18
  import { TransactionSendBloc } from '../../bloc/transaction_send/transaction_send_bloc';
20
- import { TransactionSendFormChangedEvent } from '../../bloc/transaction_send/transaction_send_event';
21
19
  import { TransactionSendFormInputState } from '../../bloc/transaction_send/transaction_send_state';
22
20
  import { TransactionFormInputAmount } from './transaction_form_input_amount';
23
21
  import { TransactionFormInputMotif } from './transaction_form_input_motif';
24
22
  import { setOthrCompte, TransactionFormInputOthr } from './transaction_form_input_othr';
23
+ import { TransactionSendFormInputPays } from './transaction_form_input_pays';
25
24
  import { TransactionSendFormInputPsp } from './transaction_form_input_psp';
26
25
 
27
26
  /**
@@ -101,20 +100,8 @@ export function TransactionFormPageOthr() {
101
100
  <View>
102
101
  <View style={styles.gap16} />
103
102
  {/* Pays */}
104
- <View style={[styles.pays, { backgroundColor: theme.cardTheme.color }]}>
105
- <Text style={theme.inputDecorationTheme.floatingLabelStyle}>{traductions.transactionFormOthrPaysLabel}</Text>
106
- <DropdownButton
107
- value={cmd.pspPays}
108
- items={UEMOACountry.liste.map((c) => ({ value: c.iso, label: `${c.flag} ${c.name}` }))}
109
- onChanged={(value) => {
110
- cmd.pspPays = value;
111
- cmd.pspCode = undefined;
112
- cmd.pspNom = undefined;
113
- transactionSendBloc.add(new TransactionSendFormChangedEvent(cmd));
114
- }}
115
- />
116
- </View>
117
- <View style={styles.gap16} />
103
+ <TransactionSendFormInputPays command={cmd} transactionSendBloc={transactionSendBloc} />
104
+ <View style={styles.gap12} />
118
105
  {/* Participant */}
119
106
  <TransactionSendFormInputPsp
120
107
  command={cmd}
@@ -185,10 +172,10 @@ function pspError(traductions: AppLocalizations, participants: Participant[], cm
185
172
 
186
173
  const styles = StyleSheet.create({
187
174
  title: { fontSize: 25 },
188
- pays: { borderRadius: 14, paddingHorizontal: 16, paddingTop: 8 },
189
175
  gap5: { height: 5 },
190
176
  gap6: { height: 6 },
191
177
  gap10: { height: 10 },
178
+ gap12: { height: 12 },
192
179
  gap16: { height: 16 },
193
180
  gap32: { height: 32 },
194
181
  });
@@ -101,7 +101,6 @@ export function TransactionVerificationPage() {
101
101
  thumbColor={theme.switchTheme.thumbColor}
102
102
  onValueChange={async () => {
103
103
  const confirmed = await showModalBottomSheet<boolean>(router, {
104
- themed: true,
105
104
  builder: () => (
106
105
  <ConfirmActionSheetWidget
107
106
  title={traductions.contactAliasSaveText}
@@ -1,13 +1,16 @@
1
+ import { MaterialIcons } from '@expo/vector-icons';
1
2
  import { useState } from 'react';
2
- import { ScrollView, StyleSheet, Text, useWindowDimensions, View } from 'react-native';
3
+ import { ScrollView, StyleSheet, Text, View } from 'react-native';
3
4
 
4
5
  import { useBloc } from '../../../../../core/bloc_react';
5
6
  import { AppRouter, useAppRouter } from '../../../../../core/router';
6
- import { useTheme } from '../../../../../core/theme';
7
+ import { Themer, useTheme } from '../../../../../core/theme';
7
8
  import { useAppLocalizations } from '../../../../../l10n/localizations';
8
9
  import { ElevatedButton } from '../../../../../shared/material/buttons';
9
10
  import { Card } from '../../../../../shared/material/card';
11
+ import { withAlpha } from '../../../../../shared/utils/color';
10
12
  import { NumberIncWidget } from '../../../../../shared/widgets/number_inc_widget';
13
+ import { SelectionSheet } from '../../../../../shared/widgets/selection';
11
14
  import { ConfigBloc } from '../../../../config/adapters/ui/bloc/config_bloc';
12
15
  import { ConfigChangeEvent } from '../../../../config/adapters/ui/bloc/config_event';
13
16
  import { ConfigKey } from '../../../../config/domain/models/config_keys';
@@ -18,7 +21,7 @@ export function TransactionRecentsNombreWidget() {
18
21
  const theme = useTheme();
19
22
  const router = useAppRouter();
20
23
  const configBloc = useBloc(ConfigBloc);
21
- const { height } = useWindowDimensions();
24
+ const primaire = theme.colorScheme.primary;
22
25
  const [nbItems] = useState(() => {
23
26
  const param = configBloc.getParamValue(ConfigKey.transactionsRecentNbItems);
24
27
  return param != null ? parseInt(param, 10) : 3;
@@ -26,47 +29,51 @@ export function TransactionRecentsNombreWidget() {
26
29
  const [updatedNbItems, setUpdatedNbItems] = useState(nbItems);
27
30
 
28
31
  return (
29
- <View style={[styles.box, { maxHeight: height * 0.5, backgroundColor: theme.colorScheme.surface }]}>
30
- <ScrollView contentContainerStyle={styles.content}>
31
- {/* Titre */}
32
- <Text style={theme.textTheme.headlineMedium}>{traductions.homeTransactionsRecentsNombreTitle}</Text>
33
- <View style={styles.gap10} />
34
- {/* Sous titre */}
35
- <Text style={theme.textTheme.displayLarge}>{traductions.homeTransactionsRecentsNombreSubTitle}</Text>
36
- <View style={styles.gap32} />
37
- {/* Boutons réduire et augmenter */}
38
- <Card>
39
- <View style={styles.row}>
40
- <Text style={[theme.textTheme.headlineSmall, styles.flex]}>{traductions.homeTransactions}</Text>
41
- <NumberIncWidget min={3} max={10} number={updatedNbItems} onChange={setUpdatedNbItems} />
42
- </View>
43
- </Card>
44
- <View style={styles.gap24} />
45
- {/* Enregistrer */}
46
- <ElevatedButton
47
- onPress={
48
- updatedNbItems !== nbItems
49
- ? () => {
50
- configBloc.add(new ConfigChangeEvent(ConfigKey.transactionsRecentNbItems, String(updatedNbItems)));
51
- AppRouter.pop(router);
52
- }
53
- : undefined
54
- }
55
- >
56
- {traductions.homeTransactionsRecentsNombreBtnSave}
57
- </ElevatedButton>
58
- <View style={styles.gap10} />
59
- </ScrollView>
60
- </View>
32
+ <SelectionSheet
33
+ titre={traductions.homeTransactionsRecentsNombreTitle}
34
+ entete={
35
+ <Text style={[theme.textTheme.displaySmall, { color: Themer.neural03Color }]}>
36
+ {traductions.homeTransactionsRecentsNombreSubTitle}
37
+ </Text>
38
+ }
39
+ contenu={
40
+ <ScrollView bounces={false} contentContainerStyle={styles.content}>
41
+ {/* Boutons réduire et augmenter */}
42
+ <Card style={styles.carte}>
43
+ <View style={styles.row}>
44
+ {/* Pastille */}
45
+ <View style={[styles.pastille, { backgroundColor: withAlpha(primaire, 0.12) }]}>
46
+ <MaterialIcons name="receipt-long" size={20} color={primaire} />
47
+ </View>
48
+ <Text style={[theme.textTheme.displayLarge, styles.libelle]}>{traductions.homeTransactions}</Text>
49
+ <NumberIncWidget min={3} max={10} number={updatedNbItems} onChange={setUpdatedNbItems} />
50
+ </View>
51
+ </Card>
52
+ <View style={styles.gap20} />
53
+ {/* Enregistrer */}
54
+ <ElevatedButton
55
+ onPress={
56
+ updatedNbItems !== nbItems
57
+ ? () => {
58
+ configBloc.add(new ConfigChangeEvent(ConfigKey.transactionsRecentNbItems, String(updatedNbItems)));
59
+ AppRouter.pop(router);
60
+ }
61
+ : undefined
62
+ }
63
+ >
64
+ {traductions.homeTransactionsRecentsNombreBtnSave}
65
+ </ElevatedButton>
66
+ </ScrollView>
67
+ }
68
+ />
61
69
  );
62
70
  }
63
71
 
64
72
  const styles = StyleSheet.create({
65
- box: { borderRadius: 20, overflow: 'hidden' },
66
- content: { padding: 20 },
67
- row: { flexDirection: 'row', alignItems: 'center', padding: 15 },
68
- flex: { flex: 1 },
69
- gap10: { height: 10 },
70
- gap24: { height: 24 },
71
- gap32: { height: 32 },
73
+ content: { paddingHorizontal: 20, paddingTop: 12, paddingBottom: 16 },
74
+ carte: { margin: 0 },
75
+ row: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 14, paddingVertical: 12 },
76
+ pastille: { width: 40, height: 40, borderRadius: 12, alignItems: 'center', justifyContent: 'center' },
77
+ libelle: { flex: 1, marginHorizontal: 14, fontWeight: '500' },
78
+ gap20: { height: 20 },
72
79
  });
@@ -59,7 +59,7 @@ export function TransactionRecentsWidget() {
59
59
  <Pressable
60
60
  accessibilityRole="button"
61
61
  hitSlop={10}
62
- onPress={() => void showModalBottomSheet(router, { themed: true, builder: () => <TransactionRecentsNombreWidget /> })}
62
+ onPress={() => void showModalBottomSheet(router, { builder: () => <TransactionRecentsNombreWidget /> })}
63
63
  >
64
64
  <MaterialIcons name="more-horiz" size={24} color={theme.colorScheme.onSurface} />
65
65
  </Pressable>
@@ -74,7 +74,6 @@ export function TransactionRtpPageActions({
74
74
 
75
75
  const onRejectTap = () =>
76
76
  void showModalBottomSheet(router, {
77
- themed: true,
78
77
  builder: () => (
79
78
  <BlocProvider blocs={[bloc]}>
80
79
  <TransactionRtpPageActionsReject />