You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

467 lines
13 KiB

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Gestion des Templates d\'Impression',
theme: ThemeData(
primarySwatch: Colors.green,
fontFamily: 'Roboto',
),
home: PrintTemplateManagementScreen(),
);
}
}
class PrintTemplate {
final int id;
final String title;
final String content;
final String createdAt;
final String updatedAt;
PrintTemplate({
required this.id,
required this.title,
required this.content,
required this.createdAt,
required this.updatedAt,
});
factory PrintTemplate.fromJson(Map<String, dynamic> json) {
return PrintTemplate(
id: json['id'],
title: json['title'],
content: json['content'],
createdAt: json['created_at'],
updatedAt: json['updated_at'],
);
}
}
class ApiResponse {
final bool success;
final List<PrintTemplate> data;
final Map<String, dynamic> pagination;
ApiResponse({
required this.success,
required this.data,
required this.pagination,
});
factory ApiResponse.fromJson(Map<String, dynamic> json) {
var dataList = json['data'] as List;
List<PrintTemplate> templates = dataList.map((item) => PrintTemplate.fromJson(item)).toList();
return ApiResponse(
success: json['success'],
data: templates,
pagination: json['pagination'],
);
}
}
class PrintTemplateManagementScreen extends StatefulWidget {
@override
_PrintTemplateManagementScreenState createState() => _PrintTemplateManagementScreenState();
}
class _PrintTemplateManagementScreenState extends State<PrintTemplateManagementScreen> {
List<PrintTemplate> templates = [];
bool isLoading = true;
String? errorMessage;
// Remplacez par votre URL d'API réelle
final String apiBaseUrl = 'https://restaurant.careeracademy.mg';
@override
void initState() {
super.initState();
fetchTemplates();
}
Future<void> fetchTemplates() async {
setState(() {
isLoading = true;
errorMessage = null;
});
try {
final response = await http.get(
Uri.parse('$apiBaseUrl/api/print-templates'),
headers: {
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
final apiResponse = ApiResponse.fromJson(json.decode(response.body));
setState(() {
templates = apiResponse.data;
isLoading = false;
});
} else {
setState(() {
errorMessage = 'Erreur HTTP: ${response.statusCode}';
isLoading = false;
});
}
} catch (e) {
print('Error fetching templates: $e');
setState(() {
errorMessage = 'Erreur de connexion: $e';
isLoading = false;
});
}
}
Future<void> updateTemplate(int id, String title, String content) async {
try {
final response = await http.put(
Uri.parse('$apiBaseUrl/api/print-templates/$id'),
headers: {
'Content-Type': 'application/json',
},
body: json.encode({
'title': title,
'content': content,
}),
);
if (response.statusCode == 200) {
// Recharger les templates après modification
await fetchTemplates();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Template modifié avec succès')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Erreur lors de la modification: ${response.statusCode}')),
);
}
} catch (e) {
print('Error updating template: $e');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Erreur de connexion: $e')),
);
}
}
// Fonction pour formater le contenu en remplaçant \r\n par des sauts de ligne
String _formatContent(String content) {
return content.replaceAll('\\r\\n', '\n').replaceAll('\\n', '\n');
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Gestion des Templates d\'Impression',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.grey[800],
),
),
SizedBox(height: 5),
Text(
'Gérez les templates d\'impression de votre système',
style: TextStyle(
fontSize: 16,
color: Colors.grey[600],
),
),
],
),
IconButton(
onPressed: fetchTemplates,
icon: Icon(Icons.refresh),
tooltip: 'Actualiser',
),
],
),
SizedBox(height: 30),
// Content
Expanded(
child: isLoading
? _buildLoadingWidget()
: errorMessage != null
? _buildErrorWidget()
: _buildTemplateTable(),
),
],
),
),
);
}
Widget _buildLoadingWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Chargement des templates...'),
],
),
);
}
Widget _buildErrorWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, size: 64, color: Colors.red),
SizedBox(height: 16),
Text(
'Erreur de chargement',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(errorMessage ?? 'Erreur inconnue'),
SizedBox(height: 16),
ElevatedButton(
onPressed: fetchTemplates,
child: Text('Réessayer'),
),
],
),
);
}
Widget _buildTemplateTable() {
if (templates.isEmpty) {
return Center(
child: Text(
'Aucun template trouvé',
style: TextStyle(fontSize: 16, color: Colors.grey[600]),
),
);
}
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: Offset(0, 2),
),
],
),
child: Column(
children: [
// Table Header
Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15),
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
),
child: Row(
children: [
Expanded(flex: 3, child: _buildHeaderCell('Titre')),
Expanded(flex: 7, child: _buildHeaderCell('Contenu')),
Expanded(flex: 1, child: _buildHeaderCell('Actions')),
],
),
),
// Table Body
Expanded(
child: ListView.builder(
itemCount: templates.length,
itemBuilder: (context, index) {
return _buildTemplateRow(templates[index]);
},
),
),
],
),
);
}
Widget _buildHeaderCell(String text) {
return Text(
text,
style: TextStyle(
fontWeight: FontWeight.w600,
color: Colors.grey[700],
fontSize: 14,
),
);
}
Widget _buildTemplateRow(PrintTemplate template) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 20), // Augmenté le padding vertical
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: Colors.grey[200]!, width: 1),
),
),
child: IntrinsicHeight( // Pour que toutes les cellules aient la même hauteur
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 3,
child: Text(
template.title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
),
),
),
Expanded(
flex: 7,
child: Container(
constraints: BoxConstraints(minHeight: 80), // Hauteur minimum pour le contenu
child: Text(
_formatContent(template.content),
style: TextStyle(
color: Colors.grey[700],
fontSize: 14,
height: 1.4, // Espacement entre les lignes
),
),
),
),
Expanded(
flex: 1,
child: Align(
alignment: Alignment.topCenter,
child: IconButton(
onPressed: () {
_showEditTemplateDialog(context, template);
},
icon: Icon(Icons.edit, color: Colors.blue, size: 20),
tooltip: 'Modifier',
),
),
),
],
),
),
);
}
void _showEditTemplateDialog(BuildContext context, PrintTemplate template) {
final _formKey = GlobalKey<FormState>();
final _titleController = TextEditingController(text: template.title);
final _contentController = TextEditingController(text: _formatContent(template.content));
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Modifier Template'),
content: Container(
width: double.maxFinite,
child: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildTextField(_titleController, 'Titre', true),
SizedBox(height: 16),
TextFormField(
controller: _contentController,
maxLines: 10, // Augmenté le nombre de lignes
decoration: InputDecoration(
labelText: 'Contenu',
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 12),
alignLabelWithHint: true,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Ce champ est requis';
}
return null;
},
),
],
),
),
),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Annuler'),
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// Reconvertir les sauts de ligne pour l'API
String contentForApi = _contentController.text.replaceAll('\n', '\\r\\n');
updateTemplate(
template.id,
_titleController.text,
contentForApi,
);
Navigator.of(context).pop();
}
},
child: Text('Modifier'),
),
],
);
},
);
}
Widget _buildTextField(TextEditingController controller, String label, bool required) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
controller: controller,
decoration: InputDecoration(
labelText: label,
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
),
validator: required ? (value) {
if (value == null || value.isEmpty) {
return 'Ce champ est requis';
}
return null;
} : null,
),
);
}
}