Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 120 additions & 39 deletions lib/features/main_screen/results_tab.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import 'dart:async' show unawaited;

import 'package:flutter/material.dart' as material;
import 'package:flutter/services.dart' show Clipboard, ClipboardData;
import 'package:querya_desktop/core/csv/result_grid_csv.dart';
import 'package:querya_desktop/core/csv/save_result_grid_csv.dart';
import 'package:querya_desktop/core/json/result_grid_json.dart';
import 'package:querya_desktop/core/json/save_result_grid_json.dart';
import 'package:querya_desktop/features/main_screen/result_grid_view.dart';
import 'package:querya_desktop/shared/services/data_export_service.dart';
import 'package:querya_desktop/shared/widgets/widgets.dart';

/// Query output: grid, loading, error, or placeholder.
Expand Down Expand Up @@ -84,67 +80,67 @@ class ResultsTab extends StatelessWidget {
size: ButtonSize.small,
onPressed: () {
unawaited(() async {
final outcome = await saveResultGridCsvFile(
await DataExportService.copyToClipboard(
DataExportFormat.csv,
columns: columns,
rows: rows,
);
if (!context.mounted) return;
if (outcome == SaveResultGridCsvOutcome.error) {
await _showSaveFileErrorDialog(context);
}
}());
},
leading: const material.Icon(
material.Icons.save_alt_rounded,
material.Icons.copy_rounded,
size: 14,
),
child: const Text('Save as CSV'),
child: const Text('Copy as CSV'),
),
OutlineButton(
size: ButtonSize.small,
onPressed: () {
unawaited(() async {
final outcome = await saveResultGridJsonFile(
await DataExportService.copyToClipboard(
DataExportFormat.json,
columns: columns,
rows: rows,
);
if (!context.mounted) return;
if (outcome == SaveResultGridJsonOutcome.error) {
await _showSaveFileErrorDialog(context);
}
}());
},
leading: const material.Icon(
material.Icons.data_object_rounded,
material.Icons.copy_rounded,
size: 14,
),
child: const Text('Save as JSON'),
child: const Text('Copy as JSON'),
),
OutlineButton(
size: ButtonSize.small,
onPressed: () {
_ExportMenuButton(
label: 'Copy formatted ▾',
icon: material.Icons.copy_all_rounded,
isSave: false,
onSelected: (format) {
unawaited(() async {
final csv = await resultGridAsCsvAsync(columns, rows);
await Clipboard.setData(ClipboardData(text: csv));
await DataExportService.copyToClipboard(
format,
columns: columns,
rows: rows,
);
}());
},
leading: const material.Icon(
material.Icons.copy_rounded,
size: 14,
),
child: const Text('Copy as CSV'),
),
OutlineButton(
size: ButtonSize.small,
onPressed: () {
final json = resultGridAsJson(columns, rows);
Clipboard.setData(ClipboardData(text: json));
_ExportMenuButton(
label: 'Save to file ▾',
icon: material.Icons.save_alt_rounded,
isSave: true,
onSelected: (format) {
unawaited(() async {
final outcome = await DataExportService.saveToFile(
format,
columns: columns,
rows: rows,
);
if (!context.mounted) return;
if (outcome == SaveExportOutcome.error) {
await _showSaveFileErrorDialog(context);
}
}());
},
leading: const material.Icon(
material.Icons.copy_rounded,
size: 14,
),
child: const Text('Copy as JSON'),
),
],
),
Expand Down Expand Up @@ -175,3 +171,88 @@ Future<void> _showSaveFileErrorDialog(material.BuildContext context) {
),
);
}

class _ExportMenuButton extends StatelessWidget {
const _ExportMenuButton({
required this.label,
required this.icon,
required this.onSelected,
required this.isSave,
});

final String label;
final material.IconData icon;
final material.ValueChanged<DataExportFormat> onSelected;
final bool isSave;

@override
Widget build(BuildContext context) {
return material.PopupMenuButton<DataExportFormat>(
tooltip: label,
onSelected: onSelected,
itemBuilder: (context) => [
material.PopupMenuItem(
value: DataExportFormat.csv,
child: material.Row(
children: [
const material.Icon(
material.Icons.table_chart_outlined, size: 16),
const material.SizedBox(width: 8),
material.Text(isSave ? 'CSV (.csv)' : 'Copy as CSV'),
],
),
),
material.PopupMenuItem(
value: DataExportFormat.json,
child: material.Row(
children: [
const material.Icon(
material.Icons.data_object_rounded, size: 16),
const material.SizedBox(width: 8),
material.Text(isSave ? 'JSON (.json)' : 'Copy as JSON'),
],
),
),
material.PopupMenuItem(
value: DataExportFormat.markdown,
child: material.Row(
children: [
const material.Icon(material.Icons.code_rounded, size: 16),
const material.SizedBox(width: 8),
material.Text(isSave ? 'Markdown Table (.md)' : 'Copy as Markdown Table'),
],
),
),
material.PopupMenuItem(
value: DataExportFormat.sqlDump,
child: material.Row(
children: [
const material.Icon(material.Icons.storage_rounded, size: 16),
const material.SizedBox(width: 8),
material.Text(isSave ? 'SQL INSERT Dump (.sql)' : 'Copy as SQL Dump'),
],
),
),
],
child: material.Container(
padding: const material.EdgeInsets.symmetric(
horizontal: 10, vertical: 6),
decoration: material.BoxDecoration(
border: material.Border.all(
color: Theme.of(context).colorScheme.border,
),
borderRadius: material.BorderRadius.circular(6),
),
child: material.Row(
mainAxisSize: material.MainAxisSize.min,
children: [
material.Icon(icon,
size: 14, color: Theme.of(context).colorScheme.foreground),
const material.SizedBox(width: 6),
Text(label).small(),
],
),
),
);
}
}
Loading
Loading