feat: setup completo architettura ticket, UI e cubit

This commit is contained in:
2026-05-06 00:08:25 +02:00
parent 0c8b9ae3ec
commit 1d45912fc7
4 changed files with 189 additions and 169 deletions

View File

@@ -47,30 +47,30 @@ class _LatestOperationsCardContent extends StatelessWidget {
borderRadius: BorderRadius.circular(16),
side: BorderSide(color: theme.dividerColor.withValues(alpha: 0.5)),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// --- HEADER DELLA CARD ---
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
child: InkWell(
onTap: () => context.push('/operations'),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// --- HEADER DELLA CARD ---
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: const Icon(
Icons.design_services_outlined,
color: color,
size: 20,
),
),
child: const Icon(
Icons.design_services_outlined,
color: color,
size: 20,
),
),
const SizedBox(width: 12),
Expanded(
child: TextButton(
onPressed: () => context.push('/operations'),
const SizedBox(width: 12),
Expanded(
child: Text(
context.l10n.homeLatestOperations,
style: TextStyle(
@@ -82,106 +82,111 @@ class _LatestOperationsCardContent extends StatelessWidget {
overflow: TextOverflow.ellipsis,
),
),
),
],
),
const SizedBox(height: 12),
],
),
const SizedBox(height: 12),
// --- CORPO DELLA CARD (LA LISTA REAL-TIME) ---
Expanded(
child:
BlocBuilder<
LatestStoreOperationsBloc,
LatestStoreOperationsState
>(
builder: (context, state) {
if (state.status == LatestStoreOperationsStatus.loading ||
state.status == LatestStoreOperationsStatus.initial) {
return const Center(child: CircularProgressIndicator());
}
// --- CORPO DELLA CARD (LA LISTA REAL-TIME) ---
Expanded(
child:
BlocBuilder<
LatestStoreOperationsBloc,
LatestStoreOperationsState
>(
builder: (context, state) {
if (state.status ==
LatestStoreOperationsStatus.loading ||
state.status ==
LatestStoreOperationsStatus.initial) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (state.status == LatestStoreOperationsStatus.failure) {
return Center(
child: Text(
"Errore di caricamento",
style: TextStyle(color: theme.colorScheme.error),
),
);
}
if (state.operations.isEmpty) {
return Center(
child: Text(
"Nessun servizio recente.",
style: TextStyle(
color: context.secondaryText,
fontStyle: FontStyle.italic,
if (state.status ==
LatestStoreOperationsStatus.failure) {
return Center(
child: Text(
"Errore di caricamento",
style: TextStyle(color: theme.colorScheme.error),
),
),
);
}
);
}
return ListView.separated(
itemCount: state.operations.length,
separatorBuilder: (context, index) => Divider(
height: 1,
color: theme.dividerColor.withValues(alpha: 0.3),
),
itemBuilder: (context, index) {
final operation = state.operations[index];
return InkWell(
onTap: () => context.push(
'/operation-form',
extra: operation,
),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0,
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 5,
child: Text(
operation.customerDisplayName ??
'Cliente sconosciuto',
style: TextStyle(
fontWeight: FontWeight.w700,
color: context.primaryText,
),
),
),
Expanded(
flex: 5,
child: Text(
operation.reference,
style: TextStyle(
fontWeight: FontWeight.w600,
color: context.primaryText,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Text(
"${operation.createdAt?.day}/${operation.createdAt?.month}",
style: TextStyle(
color: context.secondaryText,
fontSize: 12,
),
),
],
if (state.operations.isEmpty) {
return Center(
child: Text(
"Nessun servizio recente.",
style: TextStyle(
color: context.secondaryText,
fontStyle: FontStyle.italic,
),
),
);
},
);
},
),
),
],
}
return ListView.separated(
itemCount: state.operations.length,
separatorBuilder: (context, index) => Divider(
height: 1,
color: theme.dividerColor.withValues(alpha: 0.3),
),
itemBuilder: (context, index) {
final operation = state.operations[index];
return InkWell(
onTap: () => context.push(
'/operation-form',
extra: operation,
),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0,
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 5,
child: Text(
operation.customerDisplayName ??
'Cliente sconosciuto',
style: TextStyle(
fontWeight: FontWeight.w700,
color: context.primaryText,
),
),
),
Expanded(
flex: 5,
child: Text(
operation.reference,
style: TextStyle(
fontWeight: FontWeight.w600,
color: context.primaryText,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Text(
"${operation.createdAt?.day}/${operation.createdAt?.month}",
style: TextStyle(
color: context.secondaryText,
fontSize: 12,
),
),
],
),
),
);
},
);
},
),
),
],
),
),
),
);

View File

@@ -77,12 +77,13 @@ class HomeScreen extends StatelessWidget {
context: context,
),
LatestStoreOperationsCard(),
_buildDashboardWidget(
title: context.l10n.homeLatestOperationTickets,
icon: Icons.support_agent_outlined,
color: Colors.purple,
context: context,
onTap: () =>
context.push('/tickets'), // <-- Aggiunto!
),
]),
),
@@ -194,8 +195,8 @@ class HomeScreen extends StatelessWidget {
label: context.l10n.homeNewOperationTicket,
color: Colors.redAccent,
onTap: () {
// TODO: Quando avrai la rotta per la nuova assistenza
// context.push('/assistance-form');
// Andiamo alla lista! (Da lì poi aggiungeremo il tasto "+" per il form)
context.push('/tickets');
},
),
const SizedBox(width: 12),
@@ -226,68 +227,73 @@ class HomeScreen extends StatelessWidget {
required String title,
required IconData icon,
required Color color,
VoidCallback? onTap,
}) {
final theme = Theme.of(context);
return Card(
elevation: 0,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: BorderSide(color: theme.dividerColor.withValues(alpha: 0.5)),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, color: color, size: 20),
),
const SizedBox(width: 12),
Expanded(
child: Text(
title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: context.primaryText,
child: InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
child: Icon(icon, color: color, size: 20),
),
),
IconButton(
icon: Icon(
Icons.more_vert,
size: 20,
color: context.secondaryText,
const SizedBox(width: 12),
Expanded(
child: Text(
title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: context.primaryText,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
IconButton(
icon: Icon(
Icons.more_vert,
size: 20,
color: context.secondaryText,
),
onPressed: () {},
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
],
),
const Spacer(),
Center(
child: Text(
context.l10n.commonComingSoon,
style: TextStyle(
color: context.secondaryText.withValues(alpha: 0.7),
fontStyle: FontStyle.italic,
fontSize: 13,
),
onPressed: () {},
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
],
),
const Spacer(),
Center(
child: Text(
context.l10n.commonComingSoon,
style: TextStyle(
color: context.secondaryText.withValues(alpha: 0.7),
fontStyle: FontStyle.italic,
fontSize: 13,
),
),
),
const Spacer(),
],
const Spacer(),
],
),
),
),
);

View File

@@ -27,7 +27,7 @@ class TicketRepository {
.select('''
*,
customer (*),
staff (*),
staff_member (*),
target_model:model!ticket_model_id_1_fkey (*),
source_model:model!ticket_model_id_2_fkey (*)
''')
@@ -83,7 +83,7 @@ class TicketRepository {
.select('''
*,
customer (*),
staff (*),
staff_member (*),
target_model:model!ticket_model_id_1_fkey (*),
source_model:model!ticket_model_id_2_fkey (*)
''')