563 lines
17 KiB
Plaintext
563 lines
17 KiB
Plaintext
import 'dart:convert';
|
|
import 'dart:io' as io show Directory, File;
|
|
import 'package:flutter/cupertino.dart' as dom;
|
|
import 'package:flutter_quill/flutter_quill.dart' hide Text;
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_quill/flutter_quill.dart' hide Text;
|
|
import 'package:flutter_quill/quill_delta.dart';
|
|
import 'package:flutter_quill_delta_from_html/flutter_quill_delta_from_html.dart';
|
|
import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart';
|
|
|
|
import 'package:flutter_quill/flutter_quill.dart' as quill;
|
|
import 'package:html/parser.dart' show parse;
|
|
import 'package:html/dom.dart' as dom hide Element;
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_quill/flutter_quill.dart';
|
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
import 'package:flutter_quill/flutter_quill_internal.dart';
|
|
import 'package:flutter_quill/quill_delta.dart';
|
|
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:responsive_builder/responsive_builder.dart';
|
|
|
|
import '../../config/apiUrl.dart';
|
|
import '../../routes/custom_appBar.dart';
|
|
import '../../routes/custom_drawer.dart';
|
|
import '../../services/apiService.dart';
|
|
import '../../utils/auth_utils.dart';
|
|
import '../../widgets/custom_user_travel.dart';
|
|
|
|
class Template extends StatefulWidget {
|
|
final Map<String, dynamic>? templateData;
|
|
|
|
const Template({super.key, required this.templateData});
|
|
|
|
static Template fromState(GoRouterState state) {
|
|
return Template(templateData: state.extra as Map<String, dynamic>?);
|
|
}
|
|
|
|
@override
|
|
TemplateState createState() => TemplateState();
|
|
}
|
|
|
|
class TemplateState extends State<Template> {
|
|
final ApiService apiService = ApiService();
|
|
|
|
// final QuillController _controller = QuillController.basic();
|
|
String? orgId;
|
|
String? userId;
|
|
Color layoutColor = Colors.redAccent;
|
|
Color bodyColor = Colors.white;
|
|
final Map<String, TextEditingController> controllers = {};
|
|
List<String> dataHeader = ["subject"];
|
|
List<String> placeholders = [];
|
|
|
|
late QuillController _controller = QuillController.basic();
|
|
final FocusNode _focusNode = FocusNode();
|
|
|
|
late int templateId = 0;
|
|
late String templateName = "";
|
|
late List<Map<String, dynamic>> placeholderList = [];
|
|
|
|
Map<String, dynamic> get TemplateData {
|
|
final data = {
|
|
"org_id": orgId,
|
|
|
|
// "template_id": templateId,
|
|
// "template_name": controllers["templateName"]?.text,
|
|
"template_id": templateId,
|
|
"template_name": templateName,
|
|
"subject": controllers["subject"]?.text,
|
|
"body_html": jsonEncode(_controller.document.toDelta().toJson()),
|
|
|
|
// "body_html": _controller,
|
|
// "body_html": convertQuillDocToHtml(_controller.document),
|
|
// ✅ convert delta to HTML
|
|
"placeholder": jsonEncode(placeholderList),
|
|
// "created_by": userId
|
|
};
|
|
|
|
// Only add group_id if it's an edit operation
|
|
// if (widget.templateData != null && widget.templateData!.containsKey('group_id')) {
|
|
// data["template_id"] = templateData;
|
|
// }
|
|
|
|
return data;
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
for (var field in dataHeader) {
|
|
controllers[field] = TextEditingController();
|
|
}
|
|
|
|
updateData();
|
|
loadinitializeData();
|
|
loadInitialData();
|
|
}
|
|
|
|
@override
|
|
// void dispose() {
|
|
// // controllers.dispose();
|
|
// // _editorScrollController.dispose();
|
|
// _editorFocusNode.dispose();
|
|
// super.dispose();
|
|
// }
|
|
void loadinitializeData() async {
|
|
orgId = await getOrgId();
|
|
userId = await getUserId();
|
|
}
|
|
|
|
void loadInitialData() async {
|
|
String? layoutString = await getLayoutColor();
|
|
String? bodyStringColor = await getBodyColor();
|
|
|
|
setState(() {
|
|
layoutColor =
|
|
layoutString != null
|
|
? Color(int.parse(layoutString))
|
|
: Colors.redAccent;
|
|
|
|
bodyColor =
|
|
bodyStringColor != null
|
|
? Color(int.parse(bodyStringColor))
|
|
: Colors.white;
|
|
});
|
|
}
|
|
|
|
// String convertQuillDocToHtml(quill.Document doc) {
|
|
// final buffer = StringBuffer();
|
|
//
|
|
// print("convertQuillDocToHtml");
|
|
// for (final op in doc.toDelta().toList()) {
|
|
// final insert = op.data;
|
|
// final attrs = op.attributes ?? {};
|
|
//
|
|
// if (insert is String) {
|
|
// var content = insert;
|
|
//
|
|
// // Handle formatting (bold, italic, etc.)
|
|
// if (attrs.containsKey('bold')) {
|
|
// content = '<strong>$content</strong>';
|
|
// }
|
|
// if (attrs.containsKey('italic')) {
|
|
// content = '<em>$content</em>';
|
|
// }
|
|
//
|
|
// // Wrap each paragraph with <p>
|
|
// if (content.trim().isNotEmpty) {
|
|
// buffer.write('<p>${content.trim()}</p>');
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// return buffer.toString();
|
|
// }
|
|
//
|
|
// String convertQuillDocToHtml2(quill.Document doc) {
|
|
// final buffer = StringBuffer();
|
|
// final lines = <String>[];
|
|
// final delta = doc.toDelta();
|
|
//
|
|
// String applyStyles(String text, Map<String, dynamic>? attrs) {
|
|
// if (attrs == null) return text;
|
|
// if (attrs.containsKey('bold')) {
|
|
// text = '<strong>$text</strong>';
|
|
// }
|
|
// if (attrs.containsKey('italic')) {
|
|
// text = '<em>$text</em>';
|
|
// }
|
|
// return text;
|
|
// }
|
|
//
|
|
// for (final op in delta.toList()) {
|
|
// final insert = op.data;
|
|
// final attrs = op.attributes;
|
|
//
|
|
// if (insert is String) {
|
|
// final parts = insert.split('\n');
|
|
// for (int i = 0; i < parts.length; i++) {
|
|
// final part = applyStyles(parts[i], attrs);
|
|
// lines.add(part);
|
|
//
|
|
// if (i < parts.length - 1) {
|
|
// // End of line: wrap accumulated content into <p>
|
|
// final joined = lines.join('');
|
|
// if (joined.trim().isNotEmpty) {
|
|
// buffer.writeln('<p>${joined.trim()}</p>');
|
|
// }
|
|
// lines.clear();
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// // Add remaining lines
|
|
// final joined = lines.join('');
|
|
// if (joined.trim().isNotEmpty) {
|
|
// buffer.writeln('<p>${joined.trim()}</p>');
|
|
// }
|
|
//
|
|
// return buffer.toString();
|
|
// }
|
|
//
|
|
// String extractPlainTextFromHtml(String html) {
|
|
// final regex = RegExp(r'<p>(.*?)<\/p>', multiLine: true, dotAll: true);
|
|
// final matches = regex.allMatches(html);
|
|
//
|
|
// final buffer = StringBuffer();
|
|
// for (final match in matches) {
|
|
// final text = match.group(1)?.replaceAll(RegExp(r'<[^>]*>'), '') ?? '';
|
|
// buffer.writeln(text.trim());
|
|
// }
|
|
// return buffer.toString();
|
|
// }
|
|
//
|
|
// String decodeHtmlEntities(String text) {
|
|
// return text
|
|
// .replaceAll(' ', ' ')
|
|
// .replaceAll('&', '&')
|
|
// .replaceAll('<', '<')
|
|
// .replaceAll('>', '>')
|
|
// .replaceAll('"', '"')
|
|
// .replaceAll(''', "'"); // add more as needed
|
|
// }
|
|
|
|
Future<void> updateData() async {
|
|
// Ensure apiselectedUser is not null before printing
|
|
if (widget.templateData != null) {
|
|
print("API Selected User Has Data - ${widget.templateData}");
|
|
print(
|
|
"API Selected User Has - ${widget.templateData?["templateData"]?["subject"]}",
|
|
);
|
|
setState(() {
|
|
// ✅ Wrap in setState to update the UI
|
|
controllers["templateName"]?.text =
|
|
widget.templateData?["templateData"]?["template_name"] ?? "";
|
|
|
|
controllers["subject"]?.text =
|
|
widget.templateData?["templateData"]?["subject"] ?? "";
|
|
final bodyHtml =
|
|
widget.templateData?["templateData"]?["body_html"] ?? "";
|
|
|
|
print("bodyHtml - $bodyHtml");
|
|
|
|
// /*inal converter = DeltaFromHTML();
|
|
// final delta = converter.convert(bodyHtml); // Convert HTML → Delta
|
|
// final quillDoc = quill.Document.fromDelta(delta);
|
|
// */
|
|
|
|
// final plainText = extractPlainTextFromHtml(bodyHtml);
|
|
// final decodedText = decodeHtmlEntities(plainText);
|
|
//
|
|
// final quillDoc = quill.Document()..insert(0, decodedText);
|
|
// // final quillDoc = convertBasicHtmlToQuill(bodyHtml);
|
|
// // final quillDoc = quill.Document()..insert(0, plainText);
|
|
// // final quillDoc = quill.Document.fromDelta(delta);
|
|
// _controller = quill.QuillController(
|
|
// document: quillDoc,
|
|
// selection: const TextSelection.collapsed(offset: 0),
|
|
// );
|
|
|
|
final deltaJsonString =
|
|
widget.templateData?["templateData"]?["body_delta"];
|
|
if (deltaJsonString != null) {
|
|
final deltaJson = jsonDecode(deltaJsonString);
|
|
final quillDoc = quill.Document.fromJson(deltaJson);
|
|
|
|
_controller = quill.QuillController(
|
|
document: quillDoc,
|
|
selection: const TextSelection.collapsed(offset: 0),
|
|
);
|
|
}
|
|
|
|
templateName =
|
|
widget.templateData?["templateData"]?["template_name"] ?? "";
|
|
|
|
print("Fetched template_name: $templateName");
|
|
|
|
final rawPlaceholder =
|
|
widget.templateData?["templateData"]?["placeholder"];
|
|
|
|
if (rawPlaceholder is String) {
|
|
// If it's a JSON string, decode it first
|
|
placeholderList = List<Map<String, dynamic>>.from(
|
|
jsonDecode(rawPlaceholder),
|
|
);
|
|
} else if (rawPlaceholder is List) {
|
|
// If it's already a list (ideal case)
|
|
placeholderList = List<Map<String, dynamic>>.from(rawPlaceholder);
|
|
}
|
|
|
|
print("Extracted placeholders: $placeholders");
|
|
|
|
print("Fetched placeholders: $placeholderList");
|
|
|
|
templateId =
|
|
int.tryParse(
|
|
widget.templateData?["templateData"]?["template_id"]
|
|
?.toString() ??
|
|
'0',
|
|
) ??
|
|
0;
|
|
print("Fetched template_id: $templateId");
|
|
|
|
// if (widget.group?["international_policy_id"] != null) {
|
|
// selectedInternational =
|
|
// widget.group!["international_policy_id"].toString();
|
|
// }
|
|
});
|
|
} else {
|
|
print("API Selected User Has Data - No data available yet");
|
|
}
|
|
}
|
|
|
|
Future<void> handleSubmit() async {
|
|
Map<String, dynamic> data = TemplateData;
|
|
setState(() {
|
|
// updateTemplateData(data);
|
|
// This triggers UI rebuild with error messages
|
|
// if (validateData()) {
|
|
// postGroupData();
|
|
// }
|
|
});
|
|
|
|
final TemplateData1 = TemplateData;
|
|
print("TemplateData - $TemplateData1");
|
|
}
|
|
|
|
Future<void> updateTemplateData(Map<String, dynamic> policyData) async {
|
|
final String apiUrldata = '$apiUrl/api/template/update/${templateId}';
|
|
final token = await getToken(); // Fetch token
|
|
|
|
if (token == null) {
|
|
throw Exception('Token not found. Please log in.');
|
|
}
|
|
|
|
try {
|
|
final response = await http.put(
|
|
Uri.parse(apiUrldata),
|
|
headers: {
|
|
'Authorization': 'Bearer $token',
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: jsonEncode(policyData), // Convert map to JSON
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
print("policyData submitted successfully!");
|
|
print("Response: ${response.body}");
|
|
|
|
context.go('/templateList');
|
|
} else {
|
|
print("Failed to submit policyData. Status: ${response.statusCode}");
|
|
print("Error: ${response.body}");
|
|
}
|
|
} catch (e) {
|
|
print(" Error submitting policyData: $e");
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ResponsiveBuilder(
|
|
builder: (context, sizingInfo) {
|
|
bool isDesktop =
|
|
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
|
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFf5f5f5),
|
|
appBar: CustomAppBar(isDesktop: isDesktop),
|
|
drawer: CustomDrawer(isDesktop: false),
|
|
body: Padding(
|
|
padding:
|
|
isDesktop
|
|
? EdgeInsets.symmetric(
|
|
horizontal:
|
|
MediaQuery.of(context).size.width *
|
|
0.1, // 30% of screen width as horizontal padding
|
|
vertical:
|
|
MediaQuery.of(context).size.height *
|
|
0, // 5% of screen height as vertical padding
|
|
)
|
|
: EdgeInsets.all(0),
|
|
child: Row(
|
|
children: [
|
|
// if (isDesktop) CustomDrawer(isDesktop: true),
|
|
Expanded(
|
|
child: buildUserTable(
|
|
isDesktop,
|
|
context,
|
|
bodyColor,
|
|
layoutColor,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget buildUserTable(
|
|
bool isDesktop,
|
|
context,
|
|
Color? bodyColor,
|
|
Color layoutColor,
|
|
) {
|
|
return Container(
|
|
margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null,
|
|
padding: const EdgeInsets.all(28),
|
|
decoration: BoxDecoration(
|
|
color: isDesktop ? Colors.white : Color(0xFFFCFCFC),
|
|
),
|
|
child: SafeArea(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
Text("Editor"),
|
|
SizedBox(height: 10),
|
|
buildTempalteSubject(isDesktop),
|
|
|
|
SizedBox(height: 10),
|
|
buildTempalteBody(isDesktop),
|
|
Spacer(),
|
|
buildActions(isDesktop),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildTempalteSubject(bool isDesktop) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Subject",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w400,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
isFocused: false,
|
|
isDesktop: isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: controllers["subject"],
|
|
onChanged: (value) {
|
|
// _clearError("local_id_num");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "enter the subject",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildTempalteBody(bool isDesktop) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Content",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w400,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
const SizedBox(height: 10),
|
|
Container(child: QuillSimpleToolbar(controller: _controller)),
|
|
const SizedBox(height: 10),
|
|
Container(
|
|
padding: const EdgeInsets.all(16),
|
|
height: 200,
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: Colors.grey),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: QuillEditor(
|
|
controller: _controller,
|
|
scrollController: ScrollController(),
|
|
focusNode: _focusNode,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildActions(bool isDesktop) {
|
|
return Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
SizedBox(
|
|
child: ElevatedButton(
|
|
onPressed: () {
|
|
context.go('/templateList');
|
|
// You can get text from commentController.text
|
|
Navigator.of(context).pop(); // Close the modal
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.red,
|
|
// backgroundColor: widget.layoutColor,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
child: Text(
|
|
'Cancel',
|
|
style: GoogleFonts.poppins(fontSize: 13, color: Colors.white),
|
|
),
|
|
),
|
|
),
|
|
SizedBox(width: 10),
|
|
SizedBox(
|
|
child: ElevatedButton(
|
|
onPressed: () {
|
|
handleSubmit();
|
|
// You can get text from commentController.text
|
|
// Navigator.of(context).pop(); // Close the modal
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.red,
|
|
// backgroundColor: widget.layoutColor,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
child: Text(
|
|
'Save',
|
|
style: GoogleFonts.poppins(fontSize: 13, color: Colors.white),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|