rifatta operation form e diverse migliorie generali
This commit is contained in:
@@ -5,8 +5,7 @@ import 'package:flux/features/operations/blocs/operation_form_cubit.dart';
|
||||
import 'package:flux/features/operations/models/operation_model.dart';
|
||||
import 'package:flux/core/widgets/shared_forms/customer_section.dart';
|
||||
import 'package:flux/features/operations/ui/widgets/details_section.dart';
|
||||
import 'package:flux/core/widgets/shared_forms/attachments_section.dart';
|
||||
import 'package:flux/core/widgets/shared_forms/staff_section.dart';
|
||||
import 'package:flux/core/widgets/shared_forms/shared_files_section.dart'; // <- Cambiato ad un file unico per coerenza col ticket
|
||||
|
||||
class OperationFormScreen extends StatefulWidget {
|
||||
final String? operationId;
|
||||
@@ -35,6 +34,7 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
'MNP',
|
||||
'NIP',
|
||||
'UNICA',
|
||||
'FWA',
|
||||
'TELEPASS',
|
||||
'Energy',
|
||||
'Fin',
|
||||
@@ -47,10 +47,17 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// 1. Lanciamo l'inizializzazione sincrona/asincrona
|
||||
context.read<OperationFormCubit>().initForm(
|
||||
existingOperation: widget.existingOperation,
|
||||
operationId: widget.operationId,
|
||||
);
|
||||
|
||||
// 2. Lettura immediata dello stato (come fatto per il customer!)
|
||||
final currentState = context.read<OperationFormCubit>().state;
|
||||
if (currentState.status == OperationFormStatus.ready && !_isInitialized) {
|
||||
_syncTextControllers(currentState.operation);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -75,16 +82,6 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
if (_freeTextDescriptionController.text.isEmpty) {
|
||||
_freeTextDescriptionController.text = model.description ?? '';
|
||||
}
|
||||
|
||||
// Se è una nuova pratica (draft), impostiamo di default il target su OK per comodità UI
|
||||
if (model.id == null && model.status == OperationStatus.draft) {
|
||||
// Usiamo addPostFrameCallback per non interferire con il build attuale
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// Supponendo tu aggiunga la possibilità di aggiornare lo status nel metodo updateFields del Cubit
|
||||
// context.read<OperationFormCubit>().updateFields(status: OperationStatus.ok);
|
||||
});
|
||||
}
|
||||
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
@@ -103,6 +100,9 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
}) {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
_flushControllersToCubit();
|
||||
// Aggiorniamo prima lo stato bersaglio nel cubit
|
||||
context.read<OperationFormCubit>().updateFields(status: targetStatus);
|
||||
// Poi chiamiamo il salvataggio
|
||||
context.read<OperationFormCubit>().saveOperation(
|
||||
targetStatus: targetStatus,
|
||||
keepAdding: keepAdding,
|
||||
@@ -114,7 +114,8 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
if (!_formKey.currentState!.validate()) return null;
|
||||
_flushControllersToCubit();
|
||||
final attachmentsBloc = context.read<AttachmentsBloc>();
|
||||
// Presumo tu abbia creato il metodo saveOperationDraft() nel Cubit!
|
||||
|
||||
// Assicurati che questo metodo esista nel Cubit (come per il Ticket)
|
||||
final newId = await context.read<OperationFormCubit>().saveOperationDraft();
|
||||
if (newId != null && context.mounted) {
|
||||
attachmentsBloc.add(ParentEntitySavedEvent(newId));
|
||||
@@ -122,7 +123,6 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
return newId;
|
||||
}
|
||||
|
||||
// Helper per assegnare un colore agli stati
|
||||
Color _getStatusColor(OperationStatus status) {
|
||||
switch (status) {
|
||||
case OperationStatus.success:
|
||||
@@ -158,6 +158,7 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
);
|
||||
_freeTextSubtypeController.clear();
|
||||
_freeTextDescriptionController.clear();
|
||||
_referenceController.clear();
|
||||
} else if (state.status == OperationFormStatus.failure) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
@@ -176,8 +177,6 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
// Determiniamo lo stato da mostrare nel form.
|
||||
// Se è una bozza appena creata, mostriamo visivamente "OK" come default per il salvataggio.
|
||||
final displayStatus =
|
||||
state.operation.status == OperationStatus.draft &&
|
||||
state.operation.id == null
|
||||
@@ -191,7 +190,6 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
? 'Nuova Pratica - Operatore: ${state.operation.staffDisplayName}'
|
||||
: 'Modifica Pratica - Operatore: ${state.operation.staffDisplayName}',
|
||||
),
|
||||
// Mettiamo un piccolo indicatore visivo anche nella AppBar se non è OK
|
||||
actions:
|
||||
displayStatus != OperationStatus.success &&
|
||||
displayStatus != OperationStatus.draft
|
||||
@@ -214,64 +212,54 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final isUltraWide = constraints.maxWidth > 1400;
|
||||
final isDesktop = constraints.maxWidth > 900;
|
||||
if (isUltraWide) {
|
||||
return _buildUltraWide(state, theme);
|
||||
} else if (isDesktop) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 7,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: _buildMainFormContent(
|
||||
theme,
|
||||
state,
|
||||
displayStatus,
|
||||
),
|
||||
),
|
||||
),
|
||||
VerticalDivider(width: 1, color: theme.dividerColor),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: _buildNotesSection(isDesktop: true),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
child: FocusTraversalGroup(
|
||||
policy: WidgetOrderTraversalPolicy(),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final isUltraWide = constraints.maxWidth > 1400;
|
||||
final isDesktop = constraints.maxWidth > 900;
|
||||
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildMainFormContent(theme, state, displayStatus),
|
||||
const Divider(height: 32),
|
||||
_buildNotesSection(isDesktop: false),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
child: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: isUltraWide
|
||||
? 1600
|
||||
: (isDesktop ? 1200 : 800),
|
||||
),
|
||||
child: _buildResponsiveLayout(
|
||||
isUltraWide,
|
||||
isDesktop,
|
||||
state,
|
||||
displayStatus,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: SafeArea(
|
||||
child: Padding(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.scaffoldBackgroundColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, -3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
// Se c'è un KO o un blocco, cambiamo il colore del bottone principale per attirare l'attenzione
|
||||
backgroundColor:
|
||||
displayStatus != OperationStatus.success &&
|
||||
displayStatus != OperationStatus.draft
|
||||
@@ -287,8 +275,7 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
? null
|
||||
: () => _saveOperation(
|
||||
keepAdding: false,
|
||||
targetStatus:
|
||||
displayStatus, // <-- Usiamo lo stato selezionato nel form!
|
||||
targetStatus: displayStatus,
|
||||
),
|
||||
child: state.status == OperationFormStatus.saving
|
||||
? const SizedBox(
|
||||
@@ -302,7 +289,6 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
: const Text('Salva ed Esci'),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
@@ -311,8 +297,7 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
? null
|
||||
: () => _saveOperation(
|
||||
keepAdding: true,
|
||||
targetStatus:
|
||||
displayStatus, // <-- Usiamo lo stato selezionato nel form!
|
||||
targetStatus: displayStatus,
|
||||
),
|
||||
child: const Text(
|
||||
'Salva e Aggiungi Altro',
|
||||
@@ -329,72 +314,100 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildUltraWide(OperationFormState state, ThemeData theme) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
// --- LOGICA DI IMPAGINAZIONE RESPONSIVE ---
|
||||
Widget _buildResponsiveLayout(
|
||||
bool isUltraWide,
|
||||
bool isDesktop,
|
||||
OperationFormState state,
|
||||
OperationStatus displayStatus,
|
||||
) {
|
||||
if (isUltraWide) {
|
||||
// 3 COLONNE
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [_cardAnagrafica(state), _cardEsito(state)],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 24),
|
||||
Expanded(child: Column(children: [_cardDettagli(state)])),
|
||||
const SizedBox(width: 24),
|
||||
Expanded(
|
||||
child: Column(children: [_cardNote(state), _cardAllegati(state)]),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else if (isDesktop) {
|
||||
// 2 COLONNE
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildStaffSection(state),
|
||||
const Divider(height: 50),
|
||||
_buildOperationStatusSection(state),
|
||||
const Divider(height: 32),
|
||||
_buildCustomerSection(state),
|
||||
const SizedBox(height: 16),
|
||||
_buildReferenceSection(state),
|
||||
const Divider(height: 50),
|
||||
_buildOperationTypeSection(state),
|
||||
const SizedBox(height: 16),
|
||||
_buildQuantitySection(state),
|
||||
const Divider(height: 50),
|
||||
_buildDetailsSection(state),
|
||||
const Divider(height: 50),
|
||||
_cardAnagrafica(state),
|
||||
_cardEsito(state),
|
||||
_cardAllegati(state),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
VerticalDivider(width: 1, color: theme.dividerColor),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: _buildNotesSection(isDesktop: true),
|
||||
const SizedBox(width: 24),
|
||||
Expanded(
|
||||
child: Column(children: [_cardDettagli(state), _cardNote(state)]),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
// 1 COLONNA (Mobile)
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_cardAnagrafica(state),
|
||||
_cardEsito(state),
|
||||
_cardDettagli(state),
|
||||
_cardNote(state),
|
||||
_cardAllegati(state),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// --- LE CARD MODULARIZZATE E COLORATE ---
|
||||
|
||||
Widget _cardAnagrafica(OperationFormState state) {
|
||||
return _buildCard(
|
||||
title: 'Cliente e Riferimento',
|
||||
icon: Icons.person,
|
||||
themeColor: Colors.indigo,
|
||||
children: [
|
||||
SharedCustomerSection(
|
||||
customer: state.operation.customer,
|
||||
onCustomerSelected: (customer) => context
|
||||
.read<OperationFormCubit>()
|
||||
.updateFields(customer: customer),
|
||||
),
|
||||
VerticalDivider(width: 1, color: theme.dividerColor),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: _buildAttachmentSection(state),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
controller: _referenceController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Riferimento (es. Telefono, Targa...)',
|
||||
prefixIcon: Icon(Icons.tag),
|
||||
),
|
||||
validator: (v) =>
|
||||
v == null || v.isEmpty ? 'Inserisci un riferimento' : null,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStaffSection(OperationFormState state) {
|
||||
return StaffSection(
|
||||
staffId: state.operation.staffId,
|
||||
staffName: state.operation.staffDisplayName,
|
||||
onStaffSelected: (staff) => {
|
||||
context.read<OperationFormCubit>().updateFields(
|
||||
staffId: staff.id,
|
||||
staffDisplayName: staff.name,
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOperationStatusSection(OperationFormState state) {
|
||||
return Column(
|
||||
Widget _cardEsito(OperationFormState state) {
|
||||
return _buildCard(
|
||||
title: 'Esito Pratica',
|
||||
icon: Icons.fact_check,
|
||||
themeColor: _getStatusColor(state.operation.status),
|
||||
children: [
|
||||
_buildSectionTitle('Esito / Stato Operazione'),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
@@ -416,42 +429,35 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
Icons.arrow_drop_down,
|
||||
color: _getStatusColor(state.operation.status),
|
||||
),
|
||||
items: OperationStatus.values
|
||||
/* .where(
|
||||
(s) => s != OperationStatus.draft,
|
||||
) // Nascondiamo 'Bozza' dal menu */
|
||||
.map(
|
||||
(status) => DropdownMenuItem(
|
||||
value: status,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
status == OperationStatus.success
|
||||
? Icons.check_circle
|
||||
: Icons.error_outline,
|
||||
color: _getStatusColor(status),
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
status.displayName,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: _getStatusColor(status),
|
||||
),
|
||||
),
|
||||
],
|
||||
items: OperationStatus.values.map((status) {
|
||||
return DropdownMenuItem(
|
||||
value: status,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
status == OperationStatus.success
|
||||
? Icons.check_circle
|
||||
: Icons.error_outline,
|
||||
color: _getStatusColor(status),
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
status.displayName,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: _getStatusColor(status),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (newStatus) {
|
||||
if (newStatus != null) {
|
||||
// Assicurati che il metodo updateFields nel tuo Cubit accetti anche 'status'
|
||||
if (newStatus != null)
|
||||
context.read<OperationFormCubit>().updateFields(
|
||||
status: newStatus,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -459,37 +465,20 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
state.operation.status == OperationStatus.success
|
||||
? 'Lascia OK se la pratica è stata caricata con successo.'
|
||||
: 'Attenzione: la pratica verrà salvata come ${state.operation.status.displayName}.',
|
||||
? 'Lascia OK se caricata con successo.'
|
||||
: 'Attenzione: pratica salvata in stato anomalo.',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey.shade600),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCustomerSection(OperationFormState state) {
|
||||
return SharedCustomerSection(
|
||||
customer: state.operation.customer,
|
||||
onCustomerSelected: (customer) {
|
||||
context.read<OperationFormCubit>().updateFields(customer: customer);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildReferenceSection(OperationFormState state) {
|
||||
return TextFormField(
|
||||
controller: _referenceController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Riferimento (es. numero di telefono, targa...)',
|
||||
prefixIcon: Icon(Icons.tag),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOperationTypeSection(OperationFormState state) {
|
||||
return Column(
|
||||
Widget _cardDettagli(OperationFormState state) {
|
||||
return _buildCard(
|
||||
title: 'Dettagli Servizio',
|
||||
icon: Icons.design_services,
|
||||
themeColor: Colors.deepOrange,
|
||||
children: [
|
||||
_buildSectionTitle('Cosa stiamo facendo?'),
|
||||
Wrap(
|
||||
spacing: 8.0,
|
||||
runSpacing: 8.0,
|
||||
@@ -498,104 +487,140 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
label: Text(type),
|
||||
selected: state.operation.type == type,
|
||||
onSelected: (selected) {
|
||||
if (selected) {
|
||||
if (selected)
|
||||
context.read<OperationFormCubit>().setTypeWithSmartDefault(
|
||||
type,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDetailsSection(OperationFormState state) {
|
||||
return Column(
|
||||
children: [
|
||||
_buildSectionTitle('Dettagli Servizio'),
|
||||
const Divider(height: 32),
|
||||
Row(
|
||||
children: [
|
||||
const Text(
|
||||
'Quantità:',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.remove_circle_outline),
|
||||
onPressed: state.operation.quantity > 1
|
||||
? () => context.read<OperationFormCubit>().updateFields(
|
||||
quantity: state.operation.quantity - 1,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
Text(
|
||||
'${state.operation.quantity}',
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add_circle_outline),
|
||||
onPressed: () => context.read<OperationFormCubit>().updateFields(
|
||||
quantity: state.operation.quantity + 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(height: 32),
|
||||
OperationDetailsSection(
|
||||
currentOp: state.operation,
|
||||
currentType: state.operation.type,
|
||||
freeTextSubtypeController: _freeTextSubtypeController,
|
||||
freeTextDescriptionController: _freeTextDescriptionController,
|
||||
durationQuickPicks: _buildDurationQuickPicks(state.operation),
|
||||
durationQuickPicks: _buildDurationQuickPicks(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildQuantitySection(OperationFormState state) {
|
||||
return Row(
|
||||
Widget _cardNote(OperationFormState state) {
|
||||
return _buildCard(
|
||||
title: 'Note Interne',
|
||||
icon: Icons.notes,
|
||||
themeColor: Colors.teal,
|
||||
children: [
|
||||
const Text('Quantità: '),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.remove),
|
||||
onPressed: () {
|
||||
final q = state.operation.quantity;
|
||||
if (q > 1) {
|
||||
context.read<OperationFormCubit>().updateFields(quantity: q - 1);
|
||||
}
|
||||
},
|
||||
),
|
||||
Text(
|
||||
'${state.operation.quantity}',
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () {
|
||||
final q = state.operation.quantity;
|
||||
context.read<OperationFormCubit>().updateFields(quantity: q + 1);
|
||||
},
|
||||
TextFormField(
|
||||
controller: _noteController,
|
||||
maxLines: 5,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Incolla seriali, ICCID, IBAN...',
|
||||
alignLabelWithHint: true,
|
||||
fillColor: Colors.teal.withValues(alpha: 0.05),
|
||||
filled: true,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAttachmentSection(OperationFormState state) {
|
||||
return SharedAttachmentsSection(
|
||||
parentType: AttachmentParentType.operation,
|
||||
parentId: state.operation.id,
|
||||
titleForUpload: state.operation.customer?.name ?? 'Nuova pratica',
|
||||
onGenerateIdForQr: _generateIdForQr,
|
||||
Widget _cardAllegati(OperationFormState state) {
|
||||
return _buildCard(
|
||||
title: 'Allegati e Documenti',
|
||||
icon: Icons.attach_file,
|
||||
themeColor: Colors.deepPurple,
|
||||
children: [
|
||||
SharedFilesSection(
|
||||
titleNameForUpload: state.operation.customer?.name ?? 'Nuova Pratica',
|
||||
onGenerateIdForQr: _generateIdForQr,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMainFormContent(
|
||||
ThemeData theme,
|
||||
OperationFormState state,
|
||||
OperationStatus displayStatus, {
|
||||
bool showFiles = true,
|
||||
// --- WIDGET BASE PER LA CARD ---
|
||||
Widget _buildCard({
|
||||
required String title,
|
||||
required IconData icon,
|
||||
required Color themeColor,
|
||||
required List<Widget> children,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
/* _buildStaffSection(state),
|
||||
const Divider(height: 50), */
|
||||
_buildOperationStatusSection(state),
|
||||
const Divider(height: 32),
|
||||
_buildCustomerSection(state),
|
||||
const SizedBox(height: 16),
|
||||
_buildReferenceSection(state),
|
||||
const Divider(height: 50),
|
||||
_buildOperationTypeSection(state),
|
||||
const SizedBox(height: 16),
|
||||
_buildQuantitySection(state),
|
||||
const Divider(height: 50),
|
||||
_buildDetailsSection(state),
|
||||
const Divider(height: 50),
|
||||
|
||||
// QUANTITÀ
|
||||
const Divider(height: 32),
|
||||
|
||||
if (showFiles) ...[_buildAttachmentSection(state)],
|
||||
],
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 24),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
side: BorderSide(color: themeColor.withValues(alpha: 0.3), width: 1),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: themeColor.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(icon, color: themeColor),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: themeColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(height: 32),
|
||||
...children,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDurationQuickPicks(OperationModel? currentOp) {
|
||||
Widget _buildDurationQuickPicks() {
|
||||
final durations = [3, 6, 12, 24, 30, 36, 48];
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -636,46 +661,4 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNotesSection({required bool isDesktop}) {
|
||||
final title = _buildSectionTitle('Note Interne');
|
||||
final noteField = TextFormField(
|
||||
controller: _noteController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
minLines: isDesktop ? null : 5,
|
||||
maxLines: null,
|
||||
expands: isDesktop,
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Incolla qui seriali, ICCID, IBAN, indirizzi...',
|
||||
alignLabelWithHint: true,
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
);
|
||||
return isDesktop
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
title,
|
||||
const SizedBox(height: 8),
|
||||
Expanded(child: noteField),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [title, const SizedBox(height: 8), noteField],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSectionTitle(String title) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Text(
|
||||
title,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user