new entry panel fixes
Some checks failed
Release / android-build (push) Blocked by required conditions
Release / linux-build (push) Blocked by required conditions
Release / meta (push) Successful in 2s
Release / release-dev (push) Has been cancelled
Release / release-master (push) Has been cancelled

This commit is contained in:
2025-12-14 12:51:20 +00:00
parent 924c23a401
commit 13cd3cdf14
2 changed files with 128 additions and 36 deletions

View File

@@ -9,7 +9,7 @@ on:
env:
JAVA_VERSION: "17"
ANDROID_SDK_ROOT: "${{ github.workspace }}/android-sdk"
FLUTTER_CHANNEL: "stable"
FLUTTER_VERSION: "3.22.2"
BUILD_WINDOWS: "false" # set to "true" when you actually want Windows builds
GITEA_BASE_URL: https://git.tgj.services
@@ -70,11 +70,17 @@ jobs:
echo "$ANDROID_SDK_ROOT/platform-tools" >> "$GITHUB_PATH"
echo "$ANDROID_SDK_ROOT/build-tools/33.0.2" >> "$GITHUB_PATH"
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: ${{ env.FLUTTER_CHANNEL }}
cache: true
- name: Install Flutter SDK
run: |
set -euo pipefail
FLUTTER_HOME="$HOME/flutter"
if [ ! -x "$FLUTTER_HOME/bin/flutter" ]; then
rm -rf "$FLUTTER_HOME"
curl -fsSL -o /tmp/flutter.tar.xz "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz"
tar -C "$HOME" -xf /tmp/flutter.tar.xz
fi
echo "$FLUTTER_HOME/bin" >> "$GITHUB_PATH"
"$FLUTTER_HOME/bin/flutter" --version
- name: Allow all git directories (CI)
run: git config --global --add safe.directory '*'
@@ -175,11 +181,17 @@ jobs:
$SUDO apt-get update
$SUDO apt-get install -y unzip xz-utils zip libstdc++6 libglu1-mesa clang cmake ninja-build pkg-config libgtk-3-dev libsecret-1-dev liblzma-dev curl jq
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: ${{ env.FLUTTER_CHANNEL }}
cache: true
- name: Install Flutter SDK
run: |
set -euo pipefail
FLUTTER_HOME="$HOME/flutter"
if [ ! -x "$FLUTTER_HOME/bin/flutter" ]; then
rm -rf "$FLUTTER_HOME"
curl -fsSL -o /tmp/flutter.tar.xz "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz"
tar -C "$HOME" -xf /tmp/flutter.tar.xz
fi
echo "$FLUTTER_HOME/bin" >> "$GITHUB_PATH"
"$FLUTTER_HOME/bin/flutter" --version
- name: Allow all git directories (CI)
run: git config --global --add safe.directory '*'

View File

@@ -283,14 +283,53 @@ class _NewEntryPageState extends State<NewEntryPage> {
return payload;
}
Future<bool> _validateRequiredFields() async {
final missing = <String>[];
if (_useManualMileage) {
if (_startController.text.trim().isEmpty) missing.add('From');
if (_endController.text.trim().isEmpty) missing.add('To');
final mileageText = _mileageController.text.trim();
if (double.tryParse(mileageText) == null) {
missing.add('Mileage');
}
} else {
if (_routeResult == null || _routeResult!.calculatedRoute.isEmpty) {
missing.add('Route');
}
}
if (_networkController.text.trim().isEmpty) {
missing.add('Network');
}
if (missing.isEmpty) return true;
if (!mounted) return false;
final fieldList = missing.join(', ');
await showDialog<void>(
context: context,
builder: (_) => AlertDialog(
title: const Text('Required field missing'),
content: Text(
missing.length == 1
? 'Please fill the following field: $fieldList.'
: 'Please fill the following fields: $fieldList.',
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK'),
),
],
),
);
return false;
}
Future<void> _submit() async {
if (!_formKey.currentState!.validate()) return;
if (!_useManualMileage && _routeResult == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Please calculate mileage first')),
);
return;
}
if (!await _validateRequiredFields()) return;
setState(() => _submitting = true);
final api = context.read<ApiService>();
final routeStations = _routeResult?.calculatedRoute ?? [];
@@ -524,6 +563,10 @@ class _NewEntryPageState extends State<NewEntryPage> {
child: LayoutBuilder(
builder: (context, constraints) {
final twoCol = !isMobile && constraints.maxWidth > 1000;
final tractionEmpty = _tractionItems.length == 1;
final mileageEmpty = !_useManualMileage && _routeResult == null;
final balancePanels = twoCol && tractionEmpty && mileageEmpty;
final balancedHeight = balancePanels ? 165.0 : null;
final detailPanel = _section('Details', [
_buildTripSelector(context),
@@ -614,11 +657,27 @@ class _NewEntryPageState extends State<NewEntryPage> {
),
),
_buildTractionList(),
]);
], minHeight: balancedHeight);
final mileagePanel = _section(
'Mileage',
[
if (!_useManualMileage)
Align(
alignment: Alignment.centerLeft,
child: TextButton.icon(
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 6,
),
minimumSize: const Size(0, 32),
),
onPressed: _openCalculator,
icon: const Icon(Icons.calculate, size: 18),
label: const Text('Open mileage calculator'),
),
),
if (_useManualMileage)
TextFormField(
controller: _mileageController,
@@ -637,14 +696,12 @@ class _NewEntryPageState extends State<NewEntryPage> {
subtitle: Text(
'${_routeResult!.distance.toStringAsFixed(2)} mi',
),
),
if (!_useManualMileage)
Align(
alignment: Alignment.centerLeft,
child: ElevatedButton.icon(
onPressed: _openCalculator,
icon: const Icon(Icons.calculate),
label: const Text('Open mileage calculator'),
)
else
const Padding(
padding: EdgeInsets.symmetric(vertical: 8.0),
child: Text(
'No route selected. Use the calculator to add a route.',
),
),
],
@@ -656,6 +713,7 @@ class _NewEntryPageState extends State<NewEntryPage> {
_saveDraft();
},
),
minHeight: balancedHeight,
);
return SingleChildScrollView(
@@ -663,6 +721,22 @@ class _NewEntryPageState extends State<NewEntryPage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Align(
alignment: Alignment.centerRight,
child: TextButton.icon(
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: const Size(0, 36),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
onPressed: _submitting
? null
: () => _resetFormState(clearDraft: true),
icon: const Icon(Icons.clear, size: 16),
label: const Text('Clear form'),
),
),
const SizedBox(height: 8),
detailPanel,
const SizedBox(height: 16),
twoCol
@@ -682,14 +756,6 @@ class _NewEntryPageState extends State<NewEntryPage> {
],
),
const SizedBox(height: 12),
OutlinedButton.icon(
onPressed: _submitting
? null
: () => _resetFormState(clearDraft: true),
icon: const Icon(Icons.clear),
label: const Text('Clear form'),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: _submitting ? null : _submit,
icon: _submitting
@@ -791,8 +857,13 @@ class _NewEntryPageState extends State<NewEntryPage> {
);
}
Widget _section(String title, List<Widget> children, {Widget? trailing}) {
return Card(
Widget _section(
String title,
List<Widget> children, {
Widget? trailing,
double? minHeight,
}) {
Widget card = Card(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
@@ -821,6 +892,15 @@ class _NewEntryPageState extends State<NewEntryPage> {
),
),
);
if (minHeight != null) {
card = ConstrainedBox(
constraints: BoxConstraints(minHeight: minHeight),
child: card,
);
}
return card;
}
}