samre-cli 1.0.1 → 1.0.3
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.
- package/package.json +1 -1
- package/src/templates/flutter_sdk.js +185 -129
- package/src/utils/flutter.js +23 -4
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Modèle complet pour lib/samre_sdk.dart
|
|
1
|
+
// Modèle complet pour lib/samre_sdk.dart (Version 100% autonome sans dépendance au Navigator)
|
|
2
2
|
|
|
3
3
|
export function generateFlutterSdkCode({ apiUrl, apiKey, appId, appName, dureeJours }) {
|
|
4
4
|
return `// =================================================================
|
|
@@ -33,6 +33,7 @@ class SamreOverlay extends StatefulWidget {
|
|
|
33
33
|
class _SamreOverlayState extends State<SamreOverlay> {
|
|
34
34
|
int _secondsRemaining = SamreConfig.requiredDurationSeconds;
|
|
35
35
|
bool _canSubmit = false;
|
|
36
|
+
bool _showValidationDialog = false;
|
|
36
37
|
Timer? _timer;
|
|
37
38
|
|
|
38
39
|
@override
|
|
@@ -62,69 +63,91 @@ class _SamreOverlayState extends State<SamreOverlay> {
|
|
|
62
63
|
super.dispose();
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
void
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
);
|
|
66
|
+
void _openDialog() {
|
|
67
|
+
setState(() => _showValidationDialog = true);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
void _closeDialog() {
|
|
71
|
+
setState(() => _showValidationDialog = false);
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
@override
|
|
74
75
|
Widget build(BuildContext context) {
|
|
75
|
-
return
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
child:
|
|
76
|
+
return Directionality(
|
|
77
|
+
textDirection: TextDirection.ltr,
|
|
78
|
+
child: Stack(
|
|
79
|
+
children: [
|
|
80
|
+
widget.child,
|
|
81
|
+
|
|
82
|
+
// ── Bouton Flottant en Bas à Droite ──────────────────────────────
|
|
83
|
+
Positioned(
|
|
84
|
+
bottom: 24,
|
|
85
|
+
right: 20,
|
|
86
|
+
child: Material(
|
|
87
|
+
elevation: 8,
|
|
86
88
|
borderRadius: BorderRadius.circular(24),
|
|
87
|
-
|
|
88
|
-
child:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const SizedBox(width: 8),
|
|
99
|
-
Text(
|
|
100
|
-
_canSubmit ? 'Valider Journée' : 'Test en cours (\${_secondsRemaining}s)',
|
|
101
|
-
style: const TextStyle(
|
|
89
|
+
color: _canSubmit ? const Color(0xFF2563EB) : Colors.black.withOpacity(0.75),
|
|
90
|
+
child: InkWell(
|
|
91
|
+
borderRadius: BorderRadius.circular(24),
|
|
92
|
+
onTap: _canSubmit ? _openDialog : null,
|
|
93
|
+
child: Padding(
|
|
94
|
+
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
|
95
|
+
child: Row(
|
|
96
|
+
mainAxisSize: MainAxisSize.min,
|
|
97
|
+
children: [
|
|
98
|
+
Icon(
|
|
99
|
+
_canSubmit ? Icons.verified_user : Icons.timer_outlined,
|
|
102
100
|
color: Colors.white,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
size: 18,
|
|
102
|
+
),
|
|
103
|
+
const SizedBox(width: 8),
|
|
104
|
+
Text(
|
|
105
|
+
_canSubmit ? 'Valider Journée' : 'Test en cours (\${_secondsRemaining}s)',
|
|
106
|
+
style: const TextStyle(
|
|
107
|
+
color: Colors.white,
|
|
108
|
+
fontWeight: FontWeight.bold,
|
|
109
|
+
fontSize: 12,
|
|
110
|
+
decoration: TextDecoration.none,
|
|
111
|
+
),
|
|
106
112
|
),
|
|
107
|
-
|
|
108
|
-
|
|
113
|
+
],
|
|
114
|
+
),
|
|
109
115
|
),
|
|
110
116
|
),
|
|
111
117
|
),
|
|
112
118
|
),
|
|
113
|
-
|
|
114
|
-
|
|
119
|
+
|
|
120
|
+
// ── Fenêtre Modale de Validation (Indépendante du Navigator) ─────
|
|
121
|
+
if (_showValidationDialog)
|
|
122
|
+
Positioned.fill(
|
|
123
|
+
child: Container(
|
|
124
|
+
color: Colors.black.withOpacity(0.75),
|
|
125
|
+
alignment: Alignment.center,
|
|
126
|
+
padding: const EdgeInsets.symmetric(horizontal: 20),
|
|
127
|
+
child: Material(
|
|
128
|
+
color: Colors.transparent,
|
|
129
|
+
child: SamreValidationCard(
|
|
130
|
+
onClose: _closeDialog,
|
|
131
|
+
),
|
|
132
|
+
),
|
|
133
|
+
),
|
|
134
|
+
),
|
|
135
|
+
],
|
|
136
|
+
),
|
|
115
137
|
);
|
|
116
138
|
}
|
|
117
139
|
}
|
|
118
140
|
|
|
119
|
-
///
|
|
120
|
-
class
|
|
121
|
-
|
|
141
|
+
/// Carte de dialogue de validation du code quotidien
|
|
142
|
+
class SamreValidationCard extends StatefulWidget {
|
|
143
|
+
final VoidCallback onClose;
|
|
144
|
+
const SamreValidationCard({Key? key, required this.onClose}) : super(key: key);
|
|
122
145
|
|
|
123
146
|
@override
|
|
124
|
-
State<
|
|
147
|
+
State<SamreValidationCard> createState() => _SamreValidationCardState();
|
|
125
148
|
}
|
|
126
149
|
|
|
127
|
-
class
|
|
150
|
+
class _SamreValidationCardState extends State<SamreValidationCard> {
|
|
128
151
|
final _panelisteCtrl = TextEditingController();
|
|
129
152
|
final _codeCtrl = TextEditingController();
|
|
130
153
|
bool _loading = false;
|
|
@@ -200,103 +223,136 @@ class _SamreValidationDialogState extends State<SamreValidationDialog> {
|
|
|
200
223
|
|
|
201
224
|
@override
|
|
202
225
|
Widget build(BuildContext context) {
|
|
203
|
-
return
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
color: const Color(0xFF2563EB).withOpacity(0.2),
|
|
215
|
-
borderRadius: BorderRadius.circular(10),
|
|
216
|
-
),
|
|
217
|
-
child: const Icon(Icons.verified, color: Color(0xFF60A5FA), size: 22),
|
|
218
|
-
),
|
|
219
|
-
const SizedBox(width: 12),
|
|
220
|
-
const Expanded(
|
|
221
|
-
child: Text(
|
|
222
|
-
'Validation Samré',
|
|
223
|
-
style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold),
|
|
224
|
-
),
|
|
226
|
+
return Container(
|
|
227
|
+
constraints: const BoxConstraints(maxWidth: 420),
|
|
228
|
+
decoration: BoxDecoration(
|
|
229
|
+
color: const Color(0xFF0F172A),
|
|
230
|
+
borderRadius: BorderRadius.circular(24),
|
|
231
|
+
border: Border.all(color: const Color(0xFF334155), width: 1.5),
|
|
232
|
+
boxShadow: [
|
|
233
|
+
BoxShadow(
|
|
234
|
+
color: Colors.black.withOpacity(0.5),
|
|
235
|
+
blurRadius: 25,
|
|
236
|
+
spreadRadius: 5,
|
|
225
237
|
),
|
|
226
238
|
],
|
|
227
239
|
),
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
),
|
|
237
|
-
const SizedBox(height: 16),
|
|
238
|
-
TextField(
|
|
239
|
-
controller: _panelisteCtrl,
|
|
240
|
-
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w600),
|
|
241
|
-
decoration: InputDecoration(
|
|
242
|
-
labelText: 'Identifiant Panéliste',
|
|
243
|
-
labelStyle: const TextStyle(color: Color(0xFF94A3B8)),
|
|
244
|
-
hintText: 'Ex: TST-7A8B9C',
|
|
245
|
-
hintStyle: const TextStyle(color: Color(0xFF475569)),
|
|
246
|
-
filled: true,
|
|
247
|
-
fillColor: const Color(0xFF1E293B),
|
|
248
|
-
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
|
|
249
|
-
),
|
|
250
|
-
),
|
|
251
|
-
const SizedBox(height: 12),
|
|
252
|
-
TextField(
|
|
253
|
-
controller: _codeCtrl,
|
|
254
|
-
textCapitalization: TextCapitalization.characters,
|
|
255
|
-
style: const TextStyle(color: Colors.white, letterSpacing: 2, fontWeight: FontWeight.bold),
|
|
256
|
-
decoration: InputDecoration(
|
|
257
|
-
labelText: 'Code Unique du Jour',
|
|
258
|
-
labelStyle: const TextStyle(color: Color(0xFF94A3B8)),
|
|
259
|
-
hintText: 'Ex: 7K9P-4MX2',
|
|
260
|
-
hintStyle: const TextStyle(color: Color(0xFF475569), letterSpacing: 0),
|
|
261
|
-
filled: true,
|
|
262
|
-
fillColor: const Color(0xFF1E293B),
|
|
263
|
-
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
|
|
264
|
-
),
|
|
265
|
-
),
|
|
266
|
-
if (_message != null) ...[
|
|
267
|
-
const SizedBox(height: 14),
|
|
240
|
+
padding: const EdgeInsets.all(22),
|
|
241
|
+
child: Column(
|
|
242
|
+
mainAxisSize: MainAxisSize.min,
|
|
243
|
+
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
244
|
+
children: [
|
|
245
|
+
// Titre
|
|
246
|
+
Row(
|
|
247
|
+
children: [
|
|
268
248
|
Container(
|
|
269
|
-
padding: const EdgeInsets.all(
|
|
249
|
+
padding: const EdgeInsets.all(8),
|
|
270
250
|
decoration: BoxDecoration(
|
|
271
|
-
color:
|
|
251
|
+
color: const Color(0xFF2563EB).withOpacity(0.2),
|
|
272
252
|
borderRadius: BorderRadius.circular(10),
|
|
273
|
-
border: Border.all(color: _success ? const Color(0xFF10B981) : const Color(0xFFEF4444)),
|
|
274
253
|
),
|
|
254
|
+
child: const Icon(Icons.verified, color: Color(0xFF60A5FA), size: 22),
|
|
255
|
+
),
|
|
256
|
+
const SizedBox(width: 12),
|
|
257
|
+
const Expanded(
|
|
275
258
|
child: Text(
|
|
276
|
-
|
|
277
|
-
style: TextStyle(
|
|
259
|
+
'Validation Samré',
|
|
260
|
+
style: TextStyle(
|
|
261
|
+
color: Colors.white,
|
|
262
|
+
fontSize: 18,
|
|
263
|
+
fontWeight: FontWeight.bold,
|
|
264
|
+
),
|
|
278
265
|
),
|
|
279
266
|
),
|
|
267
|
+
IconButton(
|
|
268
|
+
icon: const Icon(Icons.close, color: Color(0xFF94A3B8), size: 20),
|
|
269
|
+
onPressed: widget.onClose,
|
|
270
|
+
),
|
|
280
271
|
],
|
|
272
|
+
),
|
|
273
|
+
const SizedBox(height: 12),
|
|
274
|
+
const Text(
|
|
275
|
+
'Saisissez vos identifiants pour enregistrer votre journée de test.',
|
|
276
|
+
style: TextStyle(color: Color(0xFF94A3B8), fontSize: 13),
|
|
277
|
+
),
|
|
278
|
+
const SizedBox(height: 16),
|
|
279
|
+
TextField(
|
|
280
|
+
controller: _panelisteCtrl,
|
|
281
|
+
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w600),
|
|
282
|
+
decoration: InputDecoration(
|
|
283
|
+
labelText: 'Identifiant Panéliste',
|
|
284
|
+
labelStyle: const TextStyle(color: Color(0xFF94A3B8)),
|
|
285
|
+
hintText: 'Ex: TST-7A8B9C',
|
|
286
|
+
hintStyle: const TextStyle(color: Color(0xFF475569)),
|
|
287
|
+
filled: true,
|
|
288
|
+
fillColor: const Color(0xFF1E293B),
|
|
289
|
+
border: OutlineInputBorder(
|
|
290
|
+
borderRadius: BorderRadius.circular(12),
|
|
291
|
+
borderSide: const BorderSide(color: Color(0xFF334155)),
|
|
292
|
+
),
|
|
293
|
+
),
|
|
294
|
+
),
|
|
295
|
+
const SizedBox(height: 12),
|
|
296
|
+
TextField(
|
|
297
|
+
controller: _codeCtrl,
|
|
298
|
+
textCapitalization: TextCapitalization.characters,
|
|
299
|
+
style: const TextStyle(color: Colors.white, letterSpacing: 2, fontWeight: FontWeight.bold),
|
|
300
|
+
decoration: InputDecoration(
|
|
301
|
+
labelText: 'Code Unique du Jour',
|
|
302
|
+
labelStyle: const TextStyle(color: Color(0xFF94A3B8)),
|
|
303
|
+
hintText: 'Ex: 7K9P-4MX2',
|
|
304
|
+
hintStyle: const TextStyle(color: Color(0xFF475569), letterSpacing: 0),
|
|
305
|
+
filled: true,
|
|
306
|
+
fillColor: const Color(0xFF1E293B),
|
|
307
|
+
border: OutlineInputBorder(
|
|
308
|
+
borderRadius: BorderRadius.circular(12),
|
|
309
|
+
borderSide: const BorderSide(color: Color(0xFF334155)),
|
|
310
|
+
),
|
|
311
|
+
),
|
|
312
|
+
),
|
|
313
|
+
if (_message != null) ...[
|
|
314
|
+
const SizedBox(height: 14),
|
|
315
|
+
Container(
|
|
316
|
+
padding: const EdgeInsets.all(12),
|
|
317
|
+
decoration: BoxDecoration(
|
|
318
|
+
color: _success ? const Color(0xFF065F46).withOpacity(0.3) : const Color(0xFF991B1B).withOpacity(0.3),
|
|
319
|
+
borderRadius: BorderRadius.circular(10),
|
|
320
|
+
border: Border.all(color: _success ? const Color(0xFF10B981) : const Color(0xFFEF4444)),
|
|
321
|
+
),
|
|
322
|
+
child: Text(
|
|
323
|
+
_message!,
|
|
324
|
+
style: TextStyle(
|
|
325
|
+
color: _success ? const Color(0xFF34D399) : const Color(0xFFF87171),
|
|
326
|
+
fontSize: 12,
|
|
327
|
+
fontWeight: FontWeight.w600,
|
|
328
|
+
),
|
|
329
|
+
),
|
|
330
|
+
),
|
|
281
331
|
],
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
332
|
+
const SizedBox(height: 18),
|
|
333
|
+
Row(
|
|
334
|
+
mainAxisAlignment: MainAxisAlignment.end,
|
|
335
|
+
children: [
|
|
336
|
+
TextButton(
|
|
337
|
+
onPressed: widget.onClose,
|
|
338
|
+
child: const Text('Fermer', style: TextStyle(color: Color(0xFF94A3B8))),
|
|
339
|
+
),
|
|
340
|
+
const SizedBox(width: 8),
|
|
341
|
+
ElevatedButton(
|
|
342
|
+
onPressed: _loading ? null : _verify,
|
|
343
|
+
style: ElevatedButton.styleFrom(
|
|
344
|
+
backgroundColor: const Color(0xFF2563EB),
|
|
345
|
+
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
346
|
+
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
347
|
+
),
|
|
348
|
+
child: _loading
|
|
349
|
+
? const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2))
|
|
350
|
+
: const Text('Valider', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
|
351
|
+
),
|
|
352
|
+
],
|
|
294
353
|
),
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
: const Text('Valider', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
|
298
|
-
),
|
|
299
|
-
],
|
|
354
|
+
],
|
|
355
|
+
),
|
|
300
356
|
);
|
|
301
357
|
}
|
|
302
358
|
}
|
package/src/utils/flutter.js
CHANGED
|
@@ -82,8 +82,28 @@ export const FlutterUtil = {
|
|
|
82
82
|
modified = importStatement + modified;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
// 2. Tenter d'
|
|
86
|
-
|
|
85
|
+
// 2. Tenter d'injecter dans builder de MaterialApp (Idéal pour tous les écrans et le routage)
|
|
86
|
+
if (modified.includes('MaterialApp(') && !modified.includes('SamreOverlay')) {
|
|
87
|
+
// Si MaterialApp a déjà une propriété builder
|
|
88
|
+
const builderRegex = /builder\s*:\s*\(([^)]*)\)\s*=>\s*([^,\n;]+)/;
|
|
89
|
+
if (builderRegex.test(modified)) {
|
|
90
|
+
modified = modified.replace(builderRegex, (match, args, body) => {
|
|
91
|
+
return `builder: (${args}) => SamreOverlay(child: ${body.trim()})`;
|
|
92
|
+
});
|
|
93
|
+
fs.writeFileSync(mainPath, modified, 'utf-8');
|
|
94
|
+
return { injected: true, modifiedBuilder: true, backupPath };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Sinon insérer la propriété builder juste après MaterialApp(
|
|
98
|
+
modified = modified.replace(
|
|
99
|
+
/MaterialApp\s*\(/,
|
|
100
|
+
'MaterialApp(\n builder: (context, child) => SamreOverlay(child: child ?? const SizedBox()),'
|
|
101
|
+
);
|
|
102
|
+
fs.writeFileSync(mainPath, modified, 'utf-8');
|
|
103
|
+
return { injected: true, modifiedBuilder: true, backupPath };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Fallback : injection sur home:
|
|
87
107
|
const homeRegex = /home\s*:\s*([^,\n\);]+)/;
|
|
88
108
|
if (homeRegex.test(modified) && !modified.includes('SamreOverlay')) {
|
|
89
109
|
modified = modified.replace(homeRegex, (match, p1) => {
|
|
@@ -93,8 +113,6 @@ export const FlutterUtil = {
|
|
|
93
113
|
return { injected: true, modifiedHome: true, backupPath };
|
|
94
114
|
}
|
|
95
115
|
|
|
96
|
-
// Si injection automatique du widget home n'a pas pu matcher le pattern exact,
|
|
97
|
-
// on sauvegarde le fichier avec l'import et on informe le dev
|
|
98
116
|
fs.writeFileSync(mainPath, modified, 'utf-8');
|
|
99
117
|
return { injected: false, importAdded: true, backupPath };
|
|
100
118
|
},
|
|
@@ -121,6 +139,7 @@ export const FlutterUtil = {
|
|
|
121
139
|
// Nettoyage manuel si pas de backup
|
|
122
140
|
let content = fs.readFileSync(mainPath, 'utf-8');
|
|
123
141
|
content = content.replace(/import\s+['"]samre_sdk\.dart['"];\s*\n?/g, '');
|
|
142
|
+
content = content.replace(/builder\s*:\s*\([^)]*\)\s*=>\s*SamreOverlay\s*\(\s*child\s*:\s*child\s*\?\?\s*const\s*SizedBox\(\)\s*\),?\s*\n?/g, '');
|
|
124
143
|
content = content.replace(/SamreOverlay\s*\(\s*child\s*:\s*([^)]+)\)/g, '$1');
|
|
125
144
|
fs.writeFileSync(mainPath, content, 'utf-8');
|
|
126
145
|
mainRestored = true;
|