Files
ertl_filament_verwaltung/lib/widgets/bauteil_form.dart
T
2026-10-29 15:03:48 +01:00

138 lines
5.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controller/bauteil_controller.dart';
import '../helper/input_validators.dart';
import '../theme/app_theme.dart';
import 'bauteil_filament_auswahl.dart';
import 'bauteil_photo_picker.dart';
import 'bauteil_preis_uebersicht.dart';
import 'entry_panel.dart';
class BauteilForm extends StatelessWidget {
const BauteilForm({super.key, required this.controller});
final BauteilController controller;
@override
Widget build(BuildContext context) => Obx(
() => EntryPanel(
title: controller.editingId.value == null
? 'Bauteil erfassen'
: 'Bauteil bearbeiten',
subtitle: 'Dokumentiere deinen Druck und den Verbrauch je Filament.',
child: Form(
key: controller.formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(
controller: controller.nameController,
decoration: const InputDecoration(labelText: 'Bauteilname'),
validator: InputValidators.requiredText,
),
const SizedBox(height: 16),
TextFormField(
controller: controller.verwendungController,
decoration: const InputDecoration(
labelText: 'Art der Verwendung',
),
validator: InputValidators.requiredText,
),
const SizedBox(height: 16),
EntryFieldGrid(
children: [
EntryNumberField(
controller: controller.stundenController,
label: 'Druckdauer: Stunden',
unit: 'h',
validator: InputValidators.hours,
),
EntryNumberField(
controller: controller.minutenController,
label: 'Druckdauer: Minuten',
unit: 'min',
validator: InputValidators.minutes,
),
EntryNumberField(
controller: controller.gewichtController,
label: 'Bauteilgewicht',
unit: 'g',
validator: InputValidators.positive,
),
EntryNumberField(
controller: controller.abfallController,
label: 'Abfallmenge',
unit: 'g',
validator: InputValidators.nonNegative,
),
EntryNumberField(
controller: controller.aufschlagController,
label: 'Aufschlag',
unit: '%',
validator: InputValidators.nonNegative,
),
],
),
const SizedBox(height: 12),
const Text(
'Abfall wird automatisch in die Abfallverwaltung übernommen. Seine Kosten ergeben sich aus Einkaufspreis und Startgewicht der verwendeten Rollen. Bei mehreren Filamenten wird der Abfall nach ihrem Verbrauch aufgeteilt.',
style: TextStyle(color: AppTheme.muted, height: 1.5),
),
const SizedBox(height: 24),
BauteilPhotoPicker(controller: controller),
const SizedBox(height: 24),
const Text(
'Verwendete Filamente',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w700),
),
const SizedBox(height: 6),
const Text(
'Wähle für Mehrfarbdruck mehrere Filamente. Gib je Rolle den gesamten Verbrauch inklusive ihres Abfallanteils an. Die Summe muss Bauteilgewicht + Abfall ergeben.',
style: TextStyle(color: AppTheme.muted, height: 1.5),
),
const SizedBox(height: 12),
BauteilFilamentAuswahl(controller: controller),
const SizedBox(height: 16),
BauteilPreisUebersicht(controller: controller),
const SizedBox(height: 16),
Obx(
() => FilledButton.icon(
onPressed:
controller.isSaving.value ||
controller.isPickingPhoto.value ||
controller.filamente.isEmpty
? null
: controller.saveBauteil,
icon: Icon(
controller.editingId.value == null ? Icons.add : Icons.check,
),
label: Text(
controller.isSaving.value
? 'Wird gespeichert …'
: controller.editingId.value == null
? 'Bauteil speichern'
: 'Änderungen speichern',
),
),
),
if (controller.editingId.value != null)
TextButton.icon(
onPressed: controller.isSaving.value
? null
: controller.resetForm,
icon: const Icon(Icons.close),
label: const Text('Bearbeitung abbrechen'),
),
Obx(
() => EntryMessage(
message: controller.message.value,
hasError: controller.hasError.value,
),
),
],
),
),
),
);
}