No content changes: git diff --ignore-all-space over these files is empty. The churn came from editing on Windows against a repo checked out with LF.
239 lines
7.7 KiB
Dart
239 lines
7.7 KiB
Dart
part of 'editor_screen.dart';
|
|
|
|
mixin EditorLogic on ConsumerState<EditorScreen> {
|
|
DocumentModel? _document;
|
|
|
|
// Controllers
|
|
final _tanggalCtrl = TextEditingController();
|
|
final _noPoCtrl = TextEditingController();
|
|
final _noSoCtrl = TextEditingController();
|
|
final _noDoCtrl = TextEditingController();
|
|
|
|
final _kepadaYthCtrl = TextEditingController();
|
|
final _orderUntukCtrl = TextEditingController();
|
|
final _alamatCtrl = TextEditingController();
|
|
final _platTrukCtrl = TextEditingController();
|
|
final _namaDriverCtrl = TextEditingController();
|
|
final _namaPenerimaCtrl = TextEditingController();
|
|
|
|
final _latitudeCtrl = TextEditingController();
|
|
final _longitudeCtrl = TextEditingController();
|
|
|
|
List<Map<String, TextEditingController>> _itemControllers = [];
|
|
bool _isAgreed = false;
|
|
final _formKey = GlobalKey<FormState>();
|
|
|
|
void _loadDocumentData() {
|
|
if (widget.pendingId == null) return;
|
|
|
|
final pendingDocs = ref.read(pendingDocumentsProvider);
|
|
final pendingDoc = pendingDocs.firstWhere(
|
|
(doc) => doc.id == widget.pendingId,
|
|
orElse: () => PendingDocument(id: '', imagePath: '')
|
|
);
|
|
|
|
if (pendingDoc.document != null) {
|
|
final doc = pendingDoc.document!;
|
|
setState(() {
|
|
_document = doc;
|
|
_tanggalCtrl.text = doc.tanggal;
|
|
_noPoCtrl.text = doc.noPo;
|
|
_noSoCtrl.text = doc.noSo;
|
|
_noDoCtrl.text = doc.noDo;
|
|
|
|
_kepadaYthCtrl.text = doc.kepadaYth;
|
|
_orderUntukCtrl.text = doc.orderUntuk;
|
|
_alamatCtrl.text = doc.alamat;
|
|
_platTrukCtrl.text = doc.platTruk;
|
|
_namaDriverCtrl.text = doc.namaDriver;
|
|
|
|
final lat = pendingDoc.latitude ?? doc.latitude;
|
|
final lng = pendingDoc.longitude ?? doc.longitude;
|
|
_latitudeCtrl.text = lat != null ? lat.toString() : '-';
|
|
_longitudeCtrl.text = lng != null ? lng.toString() : '-';
|
|
|
|
for (var item in doc.items) {
|
|
_addItem(
|
|
sku: item.nomorSku,
|
|
nama: item.namaBarang,
|
|
banyak: item.banyak,
|
|
jumlah: item.jumlah,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void _addItem({String sku = '', String nama = '', String banyak = '', String jumlah = ''}) {
|
|
final skuCtrl = TextEditingController(text: sku);
|
|
final namaCtrl = TextEditingController(text: nama);
|
|
final banyakCtrl = TextEditingController(text: banyak);
|
|
final jumlahCtrl = TextEditingController(text: jumlah);
|
|
|
|
setState(() {
|
|
_itemControllers.add({
|
|
'sku': skuCtrl,
|
|
'nama': namaCtrl,
|
|
'banyak': banyakCtrl,
|
|
'jumlah': jumlahCtrl,
|
|
});
|
|
});
|
|
}
|
|
|
|
void _removeItem(int index) {
|
|
setState(() {
|
|
final ctrls = _itemControllers[index];
|
|
ctrls['sku']?.dispose();
|
|
ctrls['nama']?.dispose();
|
|
ctrls['banyak']?.dispose();
|
|
ctrls['jumlah']?.dispose();
|
|
_itemControllers.removeAt(index);
|
|
});
|
|
}
|
|
|
|
void _showConfirmationDialog() {
|
|
if (!_formKey.currentState!.validate()) return;
|
|
|
|
showModalBottomSheet(
|
|
context: context,
|
|
isScrollControlled: true,
|
|
shape: const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
|
),
|
|
builder: (context) {
|
|
return ConfirmationDialog(
|
|
nameController: _namaPenerimaCtrl,
|
|
initialAgreed: _isAgreed,
|
|
onConfirm: (agreed, name) {
|
|
setState(() {
|
|
_isAgreed = agreed;
|
|
});
|
|
Navigator.pop(context);
|
|
_submitDocument();
|
|
},
|
|
);
|
|
}
|
|
);
|
|
}
|
|
|
|
Future<void> _submitDocument() async {
|
|
final pendingDocs = ref.read(pendingDocumentsProvider);
|
|
final pendingDoc = pendingDocs.firstWhere(
|
|
(doc) => doc.id == widget.pendingId,
|
|
orElse: () => PendingDocument(id: '', imagePath: '')
|
|
);
|
|
final double? finalLat = pendingDoc.latitude ?? _document?.latitude;
|
|
final double? finalLng = pendingDoc.longitude ?? _document?.longitude;
|
|
|
|
final saveAction = resolveDocumentSaveAction(
|
|
existingDocument: _document,
|
|
localImagePath: pendingDoc.imagePath,
|
|
);
|
|
|
|
if (saveAction.kind == DocumentSaveActionKind.blocked) {
|
|
if (!mounted) return;
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(saveAction.blockedMessage!),
|
|
backgroundColor: AppConfig.errorColor,
|
|
duration: const Duration(seconds: 5),
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
|
|
final apiClient = ref.read(apiClientProvider);
|
|
String serverId;
|
|
if (saveAction.kind == DocumentSaveActionKind.reuploadThenPut) {
|
|
try {
|
|
final formData = FormData.fromMap({
|
|
'image': await MultipartFile.fromFile(
|
|
pendingDoc.imagePath,
|
|
filename: pendingDoc.imagePath.split(RegExp(r'[/\\]')).last,
|
|
),
|
|
if (finalLat != null) 'latitude': finalLat.toString(),
|
|
if (finalLng != null) 'longitude': finalLng.toString(),
|
|
'scan_mode': pendingDoc.scanMode ?? 'DO',
|
|
});
|
|
final response = await apiClient.client.post(AppConfig.uploadDocumentEndpoint, data: formData);
|
|
serverId = DocumentModel.fromJson(response.data['data']).id;
|
|
} catch (e) {
|
|
if (!mounted) return;
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text('Gagal membuat dokumen di server: ${ApiException.from(e).displayMessage}'),
|
|
backgroundColor: AppConfig.errorColor,
|
|
duration: const Duration(seconds: 5),
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
} else {
|
|
serverId = saveAction.serverId!;
|
|
}
|
|
|
|
final finalDoc = DocumentModel(
|
|
id: serverId,
|
|
tanggal: _tanggalCtrl.text,
|
|
noPo: _noPoCtrl.text,
|
|
noSo: _noSoCtrl.text,
|
|
noDo: _noDoCtrl.text,
|
|
kepadaYth: _kepadaYthCtrl.text,
|
|
orderUntuk: _orderUntukCtrl.text,
|
|
alamat: _alamatCtrl.text,
|
|
platTruk: _platTrukCtrl.text,
|
|
namaDriver: _namaDriverCtrl.text,
|
|
namaPenerima: _namaPenerimaCtrl.text,
|
|
latitude: finalLat,
|
|
longitude: finalLng,
|
|
items: _itemControllers.map((ctrls) => DocumentItem(
|
|
nomorSku: ctrls['sku']!.text,
|
|
namaBarang: ctrls['nama']!.text,
|
|
banyak: ctrls['banyak']!.text,
|
|
jumlah: ctrls['jumlah']!.text,
|
|
)).toList(),
|
|
createdAt: DateTime.now(),
|
|
);
|
|
|
|
final localStorage = ref.read(localStorageProvider);
|
|
await localStorage.saveDocument(finalDoc);
|
|
|
|
bool syncedToServer = false;
|
|
String? syncError;
|
|
try {
|
|
await apiClient.client.put(
|
|
'${AppConfig.fetchDocumentsEndpoint}/${finalDoc.id}',
|
|
data: finalDoc.toPutPayload(),
|
|
);
|
|
syncedToServer = true;
|
|
} catch (e) {
|
|
syncError = ApiException.from(e).displayMessage;
|
|
}
|
|
|
|
if (widget.pendingId != null) {
|
|
if (syncedToServer) {
|
|
ref.read(pendingDocumentsProvider.notifier).removeDocument(widget.pendingId!);
|
|
} else {
|
|
ref.read(pendingDocumentsProvider.notifier).markSyncFailed(
|
|
widget.pendingId!,
|
|
finalDoc,
|
|
'Belum tersinkron ke server: $syncError',
|
|
);
|
|
}
|
|
}
|
|
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
syncedToServer
|
|
? const SnackBar(content: Text('Dokumen Berhasil Disimpan!'), backgroundColor: AppConfig.successColor)
|
|
: const SnackBar(
|
|
content: Text('Tersimpan lokal, tapi GAGAL sinkron ke server. Cek koneksi lalu retry dari daftar dokumen.'),
|
|
backgroundColor: Colors.deepOrange,
|
|
duration: Duration(seconds: 5),
|
|
),
|
|
);
|
|
context.pop(true);
|
|
}
|
|
}
|
|
}
|