templateForex file
This commit is contained in:
parent
d29caf66c5
commit
d22e8bfcac
@ -1,4 +1,6 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:html' as html;
|
||||
import 'dart:async';
|
||||
import 'dart:io' as io show Directory, File;
|
||||
import 'package:delta_to_html/delta_to_html.dart';
|
||||
import 'package:flutter/cupertino.dart' as dom;
|
||||
@ -10,11 +12,14 @@ import 'package:flutter_quill/quill_delta.dart' as quill;
|
||||
import 'package:flutter_quill_delta_from_html/flutter_quill_delta_from_html.dart';
|
||||
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
|
||||
import 'package:frontend/Screens/myTemplates/templateForex.dart'
|
||||
as _editorFocusNode;
|
||||
as _editorFocusNode;
|
||||
import 'package:frontend/Screens/myTemplates/templateForex.dart'
|
||||
as _editorScrollController;
|
||||
as _editorScrollController;
|
||||
import 'package:frontend/Screens/myTemplates/templateForex.dart' as _controller;
|
||||
import 'package:html2md/html2md.dart' as html2md;
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart';
|
||||
|
||||
import 'package:flutter_quill/flutter_quill.dart' as quill;
|
||||
@ -59,6 +64,8 @@ class TemplateForex extends StatefulWidget {
|
||||
class TemplateForexState extends State<TemplateForex> {
|
||||
final ApiService apiService = ApiService();
|
||||
|
||||
Uint8List? _imageBytes;
|
||||
String? selectedOrglogo;
|
||||
// final QuillController _controller = QuillController.basic();
|
||||
String? orgId;
|
||||
String? userId;
|
||||
@ -91,11 +98,6 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
"body_html": DeltaToHTML.encodeJson(
|
||||
_controller.document.toDelta().toJson(),
|
||||
),
|
||||
// "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
|
||||
};
|
||||
@ -194,14 +196,14 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
|
||||
setState(() {
|
||||
layoutColor =
|
||||
layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
|
||||
bodyColor =
|
||||
bodyStringColor != null
|
||||
? Color(int.parse(bodyStringColor))
|
||||
: Colors.white;
|
||||
bodyStringColor != null
|
||||
? Color(int.parse(bodyStringColor))
|
||||
: Colors.white;
|
||||
});
|
||||
}
|
||||
|
||||
@ -349,10 +351,10 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
.split('_') // split by underscore
|
||||
.map(
|
||||
(word) =>
|
||||
word.isNotEmpty
|
||||
? '${word[0].toUpperCase()}${word.substring(1)}'
|
||||
: '',
|
||||
)
|
||||
word.isNotEmpty
|
||||
? '${word[0].toUpperCase()}${word.substring(1)}'
|
||||
: '',
|
||||
)
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
@ -363,7 +365,7 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
print(
|
||||
"API Selected User Has - ${widget.templateData?["templateData"]?["subject"]}",
|
||||
);
|
||||
setState(() {
|
||||
setState(() async {
|
||||
// ✅ Wrap in setState to update the UI
|
||||
controllers["templateName"]?.text =
|
||||
widget.templateData?["templateData"]?["template_name"] ?? "";
|
||||
@ -408,7 +410,7 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
print("Fetched template_name: $templateName");
|
||||
|
||||
final rawPlaceholder =
|
||||
widget.templateData?["templateData"]?["placeholder"];
|
||||
widget.templateData?["templateData"]?["placeholder"];
|
||||
|
||||
if (rawPlaceholder is String) {
|
||||
// If it's a JSON string, decode it first
|
||||
@ -427,12 +429,12 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
templateId =
|
||||
int.tryParse(
|
||||
widget.templateData?["templateData"]?["template_id"]
|
||||
?.toString() ??
|
||||
?.toString() ??
|
||||
'0',
|
||||
) ??
|
||||
0;
|
||||
0;
|
||||
print("Fetched template_id: $templateId");
|
||||
|
||||
fetchSignature();
|
||||
// if (widget.group?["international_policy_id"] != null) {
|
||||
// selectedInternational =
|
||||
// widget.group!["international_policy_id"].toString();
|
||||
@ -443,6 +445,34 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchSignature() async {
|
||||
final uri = Uri.parse('$apiUrl/api/getForexSignaturePath');
|
||||
final token = await getToken();
|
||||
|
||||
final response = await http.get(
|
||||
uri,
|
||||
headers: {'Authorization': 'Bearer $token'},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
print("ERS - $response");
|
||||
final json = jsonDecode(response.body);
|
||||
|
||||
print("ERSjson - $json");
|
||||
|
||||
String? rawLogoPath = json['url']?.toString();
|
||||
|
||||
if (rawLogoPath != null && rawLogoPath.isNotEmpty) {
|
||||
print("ERSrawLogoPath - $rawLogoPath");
|
||||
setState(() {
|
||||
selectedOrglogo = rawLogoPath;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
print("❌ Failed to fetch signature: ${response.statusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> handleSubmit() async {
|
||||
Map<String, dynamic> data = TemplateData;
|
||||
|
||||
@ -492,6 +522,7 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
// 'app-signature': 'ts-traveltool-2025-signature-123456',
|
||||
},
|
||||
body: jsonEncode(policyData), // Convert map to JSON
|
||||
);
|
||||
@ -519,6 +550,70 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _pickImage() async {
|
||||
final picker = ImagePicker();
|
||||
final XFile? pickedFile = await picker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
);
|
||||
|
||||
if (pickedFile != null && kIsWeb) {
|
||||
try {
|
||||
final bytes = await pickedFile.readAsBytes();
|
||||
print('✅ Image loaded, size: ${bytes.length} bytes');
|
||||
setState(() {
|
||||
_imageBytes = bytes;
|
||||
});
|
||||
await uploadSignature();
|
||||
} catch (e) {
|
||||
print('❌ Error reading image bytes: $e');
|
||||
}
|
||||
} else {
|
||||
print('⚠️ Image picking canceled or not on web.');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> uploadSignature() async {
|
||||
if (_imageBytes == null) {
|
||||
print('⚠️ No image selected');
|
||||
return;
|
||||
}
|
||||
|
||||
final token = await getToken(); // Fetch token
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
final uri = Uri.parse('$apiUrl/api/forex_signature_upload');
|
||||
final request = http.MultipartRequest('POST', uri);
|
||||
|
||||
// Add auth header
|
||||
request.headers['Authorization'] = 'Bearer $token';
|
||||
|
||||
// Add the image as multipart with the key "signature"
|
||||
request.files.add(
|
||||
http.MultipartFile.fromBytes(
|
||||
'signature', // <-- key name
|
||||
_imageBytes!, // <-- image bytes
|
||||
filename: 'signature.png', // <-- filename (can be png/jpg)
|
||||
contentType: MediaType('image', 'png'),
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
final response = await request.send();
|
||||
final respStr = await response.stream.bytesToString();
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
print('✅ Upload successful: $respStr');
|
||||
} else {
|
||||
print('❌ Upload failed (${response.statusCode}): $respStr');
|
||||
}
|
||||
} catch (e) {
|
||||
print('❌ Error uploading signature: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(
|
||||
@ -532,16 +627,16 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
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),
|
||||
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),
|
||||
@ -562,11 +657,11 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
}
|
||||
|
||||
Widget buildUserTable(
|
||||
bool isDesktop,
|
||||
context,
|
||||
Color? bodyColor,
|
||||
Color layoutColor,
|
||||
) {
|
||||
bool isDesktop,
|
||||
context,
|
||||
Color? bodyColor,
|
||||
Color layoutColor,
|
||||
) {
|
||||
return Container(
|
||||
margin: isDesktop ? const EdgeInsets.only(top: 5.0, bottom: 10.0) : null,
|
||||
padding: const EdgeInsets.all(28),
|
||||
@ -683,56 +778,65 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.8 : null,
|
||||
child: IconTheme(
|
||||
data: IconThemeData(size: 18), // Set icon size here
|
||||
child: QuillSimpleToolbar(
|
||||
controller: _controller,
|
||||
config: QuillSimpleToolbarConfig(
|
||||
embedButtons: FlutterQuillEmbeds.toolbarButtons(),
|
||||
showClipboardPaste: true,
|
||||
customButtons: [
|
||||
QuillToolbarCustomButtonOptions(
|
||||
icon: const Icon(Icons.add_alarm_rounded),
|
||||
onPressed: () {
|
||||
_controller.document.insert(
|
||||
_controller.selection.extentOffset,
|
||||
TimeStampEmbed(DateTime.now().toString()),
|
||||
);
|
||||
|
||||
_controller.updateSelection(
|
||||
TextSelection.collapsed(
|
||||
offset: _controller.selection.extentOffset + 1,
|
||||
),
|
||||
ChangeSource.local,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
buttonOptions: QuillSimpleToolbarButtonOptions(
|
||||
base: QuillToolbarBaseButtonOptions(
|
||||
afterButtonPressed: () {
|
||||
final isDesktop = {
|
||||
TargetPlatform.linux,
|
||||
TargetPlatform.windows,
|
||||
TargetPlatform.macOS,
|
||||
}.contains(defaultTargetPlatform);
|
||||
// if (isDesktop) {
|
||||
// _editorFocusNode.requestFocus();
|
||||
// }
|
||||
},
|
||||
),
|
||||
linkStyle: QuillToolbarLinkStyleButtonOptions(
|
||||
validateLink: (link) {
|
||||
// Treats all links as valid. When launching the URL,
|
||||
// `https://` is prefixed if the link is incomplete (e.g., `google.com` → `https://google.com`)
|
||||
// however this happens only within the editor.
|
||||
return true;
|
||||
},
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
color: Color(0xFFFFFEF0),
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.8 : null,
|
||||
child: IconTheme(
|
||||
data: IconThemeData(size: 18), // Set icon size here
|
||||
child: QuillSimpleToolbar(controller: _controller),
|
||||
),
|
||||
),
|
||||
// child: QuillSimpleToolbar(
|
||||
// controller: _controller,
|
||||
// config: QuillSimpleToolbarConfig(
|
||||
// embedButtons: FlutterQuillEmbeds.toolbarButtons(),
|
||||
// showClipboardPaste: true,
|
||||
// customButtons: [
|
||||
// QuillToolbarCustomButtonOptions(
|
||||
// icon: const Icon(Icons.add_alarm_rounded),
|
||||
// onPressed: () {
|
||||
// _controller.document.insert(
|
||||
// _controller.selection.extentOffset,
|
||||
// TimeStampEmbed(DateTime.now().toString()),
|
||||
// );
|
||||
//
|
||||
// _controller.updateSelection(
|
||||
// TextSelection.collapsed(
|
||||
// offset: _controller.selection.extentOffset + 1,
|
||||
// ),
|
||||
// ChangeSource.local,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// buttonOptions: QuillSimpleToolbarButtonOptions(
|
||||
// base: QuillToolbarBaseButtonOptions(
|
||||
// afterButtonPressed: () {
|
||||
// final isDesktop = {
|
||||
// TargetPlatform.linux,
|
||||
// TargetPlatform.windows,
|
||||
// TargetPlatform.macOS,
|
||||
// }.contains(defaultTargetPlatform);
|
||||
// // if (isDesktop) {
|
||||
// // _editorFocusNode.requestFocus();
|
||||
// // }
|
||||
// },
|
||||
// ),
|
||||
// linkStyle: QuillToolbarLinkStyleButtonOptions(
|
||||
// validateLink: (link) {
|
||||
// // Treats all links as valid. When launching the URL,
|
||||
// // `https://` is prefixed if the link is incomplete (e.g., `google.com` → `https://google.com`)
|
||||
// // however this happens only within the editor.
|
||||
// return true;
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 2),
|
||||
Container(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
@ -743,7 +847,7 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
context: context,
|
||||
builder:
|
||||
(context) =>
|
||||
PlaceholdersModal(placeholders: placeholderList),
|
||||
PlaceholdersModal(placeholders: placeholderList),
|
||||
);
|
||||
|
||||
if (selected != null) {
|
||||
@ -782,9 +886,10 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
height: MediaQuery.of(context).size.height * 0.45,
|
||||
height: MediaQuery.of(context).size.height * 0.38,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: Color(0xFFD6D5E6), width: 0.5),
|
||||
@ -799,15 +904,16 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
padding: const EdgeInsets.all(16),
|
||||
embedBuilders: [
|
||||
...FlutterQuillEmbeds.editorBuilders(
|
||||
imageEmbedConfig: QuillEditorImageEmbedConfig(
|
||||
imageProviderBuilder: (context, imageUrl) {
|
||||
// https://pub.dev/packages/flutter_quill_extensions#-image-assets
|
||||
if (imageUrl.startsWith('assets/')) {
|
||||
return AssetImage(imageUrl);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
// imageEmbedConfig: QuillEditorImageEmbedConfig(
|
||||
// imageProviderBuilder: (context, imageUrl) {
|
||||
// if (imageUrl.startsWith('data:image')) {
|
||||
// return MemoryImage(
|
||||
// base64Decode(imageUrl.split(',').last),
|
||||
// );
|
||||
// }
|
||||
// return null;
|
||||
// },
|
||||
// ),
|
||||
videoEmbedConfig: QuillEditorVideoEmbedConfig(
|
||||
customVideoBuilder: (videoUrl, readOnly) {
|
||||
// To load YouTube videos https://github.com/singerdmx/flutter-quill/releases/tag/v10.8.0
|
||||
@ -820,6 +926,58 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Container(
|
||||
child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
"Upload Signature",
|
||||
style: GoogleFonts.poppins(fontSize: 11.5),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
GestureDetector(
|
||||
onTap: _pickImage,
|
||||
|
||||
child:
|
||||
_imageBytes != null
|
||||
? ClipOval(
|
||||
child: Image.memory(
|
||||
_imageBytes!,
|
||||
// width: 50,
|
||||
// height: 50,
|
||||
width: 50, // Use responsive width
|
||||
height: 50,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
)
|
||||
: selectedOrglogo != null
|
||||
? ClipRect(
|
||||
child: Image.network(
|
||||
selectedOrglogo!,
|
||||
width: 50, // Use responsive width
|
||||
height: 50,
|
||||
// width: 250,
|
||||
// height: 55,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return const CircleAvatar(
|
||||
radius: 20,
|
||||
backgroundColor: Colors.redAccent,
|
||||
child: Icon(Icons.error, size: 10),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
: const CircleAvatar(
|
||||
radius: 20,
|
||||
backgroundColor: Colors.amber,
|
||||
child: Icon(Icons.add_a_photo, size: 10),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -859,17 +1017,17 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
SizedBox(
|
||||
child: ElevatedButton(
|
||||
onPressed:
|
||||
isDisable
|
||||
? null
|
||||
: () async {
|
||||
setState(() {
|
||||
isDisable = true;
|
||||
});
|
||||
isDisable
|
||||
? null
|
||||
: () async {
|
||||
setState(() {
|
||||
isDisable = true;
|
||||
});
|
||||
|
||||
await handleSubmit();
|
||||
// You can get text from commentController.text
|
||||
// Navigator.of(context).pop(); // Close the modal
|
||||
},
|
||||
await handleSubmit();
|
||||
// You can get text from commentController.text
|
||||
// Navigator.of(context).pop(); // Close the modal
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: layoutColor,
|
||||
// backgroundColor: widget.layoutColor,
|
||||
@ -924,4 +1082,4 @@ class TimeStampEmbedBuilder extends EmbedBuilder {
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
1085
lib/Screens/myTemplates/templateForexOld.dart
Normal file
1085
lib/Screens/myTemplates/templateForexOld.dart
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user