Kontaktok listázási hibájának javítása, csv export
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:terepi_seged/services/project_service.dart';
|
||||
|
||||
import '../../../../services/contact_service.dart';
|
||||
|
||||
@@ -27,16 +28,22 @@ class ContactsController extends GetxController {
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Future<void> load() async {
|
||||
isLoading.value = true;
|
||||
Future<void> load({bool silent = false}) async {
|
||||
final projectId = ProjectService.to.activeProject.value?.uuid;
|
||||
if (projectId == null) {
|
||||
error.value = 'Nincs aktív projekt.';
|
||||
isLoading.value = false;
|
||||
return;
|
||||
}
|
||||
if (!silent) isLoading.value = true;
|
||||
error.value = '';
|
||||
try {
|
||||
items.value = await ContactService.to.listMerged();
|
||||
pendingCount.value = await ContactService.to.pendingCount();
|
||||
items.value = await ContactService.to.listMerged(projectId);
|
||||
pendingCount.value = await ContactService.to.pendingCount(projectId);
|
||||
} catch (e) {
|
||||
error.value = _friendly(e);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
if (!silent) isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +67,10 @@ class ContactsController extends GetxController {
|
||||
await ContactService.to
|
||||
.delete(id: it.contact.id, localUuid: it.localUuid);
|
||||
items.remove(it);
|
||||
pendingCount.value = await ContactService.to.pendingCount();
|
||||
final projectId = ProjectService.to.activeProject.value?.uuid;
|
||||
if (projectId != null) {
|
||||
pendingCount.value = await ContactService.to.pendingCount(projectId);
|
||||
}
|
||||
} catch (e) {
|
||||
Get.snackbar('Hiba', _friendly(e), snackPosition: SnackPosition.BOTTOM);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:terepi_seged/pages/contacts/presentation/controllers/contacts_controller.dart';
|
||||
import 'package:terepi_seged/services/project_service.dart';
|
||||
|
||||
import '../../../../models/contact.dart';
|
||||
import '../../../../services/contact_service.dart';
|
||||
@@ -57,7 +59,11 @@ class _ContactEditViewState extends State<ContactEditView> {
|
||||
if (!(_formKey.currentState?.validate() ?? false)) return;
|
||||
setState(() => _saving = true);
|
||||
try {
|
||||
final contact = (_original ?? const Contact(name: '')).copyWith(
|
||||
final contact = (_original ??
|
||||
Contact(
|
||||
name: '',
|
||||
projectId: ProjectService.to.activeProject.value!.uuid))
|
||||
.copyWith(
|
||||
name: _name.text,
|
||||
address: _address.text,
|
||||
phone: _phone.text,
|
||||
@@ -66,6 +72,9 @@ class _ContactEditViewState extends State<ContactEditView> {
|
||||
);
|
||||
final queued = await ContactService.to.save(contact);
|
||||
Get.back(result: queued); // a lista frissítéshez visszakapja
|
||||
if (Get.isRegistered<ContactsController>()) {
|
||||
Get.find<ContactsController>().load(silent: true);
|
||||
}
|
||||
Get.snackbar(queued ? 'Elmentve (offline)' : 'Mentve',
|
||||
queued ? '${contact.name} - feltöltés, amint van net' : contact.name,
|
||||
snackPosition: SnackPosition.BOTTOM);
|
||||
|
||||
@@ -3,6 +3,9 @@ import 'dart:io';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:terepi_seged/models/project.dart';
|
||||
import 'package:terepi_seged/services/contact_export_service.dart';
|
||||
import 'package:terepi_seged/services/project_service.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../../../models/contact.dart';
|
||||
@@ -29,18 +32,42 @@ class ContactsView extends StatelessWidget {
|
||||
body: const _NoAccess(),
|
||||
);
|
||||
}
|
||||
// Projekt-kapu — a kapcsolat mindig egy projekthez tartozik, és csak
|
||||
// MEGOSZTOTT (online) projektnél van értelme (a funkció a Supabase-en
|
||||
// él). Csak-lokális vagy hiányzó projektnél blokkolunk.
|
||||
final activeProject = ProjectService.to.activeProject.value;
|
||||
if (activeProject == null || activeProject.isLocalOnly) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Kapcsolatok')),
|
||||
body: _ProjectRequired(project: activeProject),
|
||||
);
|
||||
}
|
||||
|
||||
final c = Get.put(ContactsController());
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Kapcsolatok'),
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('Kapcsolatok'),
|
||||
Text(activeProject.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 12, fontWeight: FontWeight.normal)),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.upload_file),
|
||||
tooltip: 'Import CSV',
|
||||
onPressed: () => _importCsv(c),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.ios_share),
|
||||
tooltip: 'Export CSV',
|
||||
onPressed: () => _exportDialog(c, activeProject.name),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
tooltip: 'Frissítés',
|
||||
@@ -143,8 +170,9 @@ class ContactsView extends StatelessWidget {
|
||||
snackPosition: SnackPosition.BOTTOM);
|
||||
return;
|
||||
}
|
||||
|
||||
final parsed = await ContactService.parseCsv(File(path));
|
||||
final activeProject = ProjectService.to.activeProject.value;
|
||||
final parsed = await ContactService.parseCsv(File(path),
|
||||
projectId: activeProject!.uuid);
|
||||
if (parsed.isEmpty) {
|
||||
Get.snackbar('Import', 'Nem található érvényes sor a fájlban.',
|
||||
snackPosition: SnackPosition.BOTTOM);
|
||||
@@ -167,15 +195,59 @@ class ContactsView extends StatelessWidget {
|
||||
));
|
||||
if (ok != true) return;
|
||||
|
||||
final inserted = await ContactService.to.importMany(parsed);
|
||||
final imported = await ContactService.to.importMany(parsed);
|
||||
await c.load();
|
||||
Get.snackbar('Import kész', '$inserted kapcsolat importálva.',
|
||||
snackPosition: SnackPosition.BOTTOM);
|
||||
Get.snackbar(
|
||||
'Import kész',
|
||||
imported.queued > 0
|
||||
? '${imported.uploaded} feltöltve, ${imported.queued} offline '
|
||||
'várólistán (net jöttével automatikusan felmegy).'
|
||||
: '${imported.uploaded} kapcsolat importálva.',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
} catch (e) {
|
||||
Get.snackbar('Import hiba', e.toString(),
|
||||
snackPosition: SnackPosition.BOTTOM);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _exportDialog(ContactsController c, String projectName) async {
|
||||
final hungarian = true.obs;
|
||||
|
||||
await Get.dialog(AlertDialog(
|
||||
title: const Text('Export CSV'),
|
||||
content: Obx(() => SwitchListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
dense: true,
|
||||
title: const Text('Magyar Excel-formátum'),
|
||||
subtitle: const Text('pontosvessző elválasztó',
|
||||
style: TextStyle(fontSize: 11)),
|
||||
value: hungarian.value,
|
||||
onChanged: (v) => hungarian.value = v,
|
||||
)),
|
||||
actions: [
|
||||
TextButton(onPressed: Get.back, child: const Text('Mégse')),
|
||||
FilledButton.icon(
|
||||
icon: const Icon(Icons.ios_share, size: 18),
|
||||
label: const Text('Export'),
|
||||
onPressed: () async {
|
||||
Get.back();
|
||||
try {
|
||||
await ContactExportService.exportAndShare(
|
||||
contacts: c.filtered.map((it) => it.contact).toList(),
|
||||
// contacts: c.items.map((it) => it.contact).toList(),
|
||||
hungarian: hungarian.value,
|
||||
projectName: projectName,
|
||||
);
|
||||
} catch (e) {
|
||||
Get.snackbar('Export', e.toString(),
|
||||
snackPosition: SnackPosition.BOTTOM);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Sor ──────────────────────────────────────────────────────────────
|
||||
@@ -333,3 +405,35 @@ class _ErrorState extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ProjectRequired extends StatelessWidget {
|
||||
final Project? project;
|
||||
const _ProjectRequired({required this.project});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isLocalOnly = project?.isLocalOnly ?? false;
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.folder_off_outlined,
|
||||
size: 48, color: Colors.grey.shade400),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
project == null
|
||||
? 'Nincs aktív projekt.\nVálassz egyet a menüből.'
|
||||
: 'A(z) "${project!.name}" projekt csak lokális.\n'
|
||||
'A kapcsolatok megosztott (online) projekthez '
|
||||
'tartoznak — válts megosztott projektre, vagy tedd '
|
||||
'megosztottá ezt.',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:math' as math;
|
||||
import 'dart:ui' show FontFeature;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
//import 'package:flutter/widget_previews.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:terepi_seged/pages/map_survey/presentations/widgets/stakeout_list_sheet.dart';
|
||||
import 'package:terepi_seged/pages/map_survey/presentations/widgets/stakeout_settings_dialog.dart';
|
||||
@@ -619,6 +620,7 @@ class _DevRow extends StatelessWidget {
|
||||
required this.posText,
|
||||
required this.negText});
|
||||
|
||||
//@Preview(name: 'Example')
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cm = value.abs() * 100;
|
||||
|
||||
Reference in New Issue
Block a user