refactor dashboard note list

This commit is contained in:
2026-05-30 16:45:12 +02:00
parent 6394e5a2cd
commit b69308e1ef
6 changed files with 125 additions and 12 deletions

View File

@@ -0,0 +1,73 @@
import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/features/notes/data/notes_repository.dart';
import 'package:flux/features/notes/models/note_model.dart';
import 'package:get_it/get_it.dart';
part 'dashboard_note_list_state.dart';
class DashboardNoteListCubit extends Cubit<DashboardNoteListState> {
final NotesRepository _repository = GetIt.I.get<NotesRepository>();
final String? companyId;
final String? staffId;
StreamSubscription<void>? _subscription;
DashboardNoteListCubit({required this.companyId, required this.staffId})
: super(DashboardNoteListState(status: DashboardNoteListStatus.initial));
void stopListening() {
_subscription?.cancel();
_subscription = null;
}
void startListening() {
stopListening();
emit(state.copyWith(status: DashboardNoteListStatus.loading));
// Primo caricamento
_loadNotesSilently();
// Inizio ascolto campanello
try {
_subscription = _repository
.notesStream(companyId: companyId!, currentStaffId: staffId!)
.listen((_) {
// Quando il campanello suona (qualcosa è cambiato a DB), ricarichiamo!
_loadNotesSilently();
});
} on Exception catch (e) {
debugPrint(e.toString());
}
}
Future<void> _loadNotesSilently() async {
try {
final notes = await _repository.getNotes();
emit(
state.copyWith(
status: DashboardNoteListStatus.success,
notes: notes,
errorMessage: null,
),
);
} catch (e) {
emit(
state.copyWith(
status: DashboardNoteListStatus.failure,
errorMessage: e.toString(),
),
);
}
}
@override
Future<void> close() {
stopListening();
return super.close();
}
}

View File

@@ -0,0 +1,30 @@
part of 'dashboard_note_list_cubit.dart';
enum DashboardNoteListStatus { initial, loading, success, failure }
class DashboardNoteListState extends Equatable {
final DashboardNoteListStatus status;
final List<NoteModel> notes;
final String? errorMessage;
const DashboardNoteListState({
this.status = DashboardNoteListStatus.initial,
this.notes = const [],
this.errorMessage,
});
DashboardNoteListState copyWith({
DashboardNoteListStatus? status,
List<NoteModel>? notes,
String? errorMessage,
}) {
return DashboardNoteListState(
status: status ?? this.status,
notes: notes ?? this.notes,
errorMessage: errorMessage ?? this.errorMessage,
);
}
@override
List<Object?> get props => [status, notes, errorMessage];
}

View File

@@ -0,0 +1,197 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/core/routes/routes.dart';
import 'package:flux/core/theme/theme.dart';
import 'package:flux/features/home/dashboard_note_list/blocs/dashboard_note_list_cubit.dart';
import 'package:flux/features/notes/models/note_model.dart';
import 'package:go_router/go_router.dart'; // Supponendo tu usi GoRouter per la navigazione
class DashboardNoteListCard extends StatelessWidget {
const DashboardNoteListCard({super.key});
@override
Widget build(BuildContext context) {
return Card(
elevation: 0,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: BorderSide(
color: Theme.of(context).dividerColor.withValues(alpha: 0.3),
style: BorderStyle.solid,
),
),
child: InkWell(
onTap: () => context.pushNamed(Routes.notes),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Intestazione del riquadro
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.yellow.shade700.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.sticky_note_2_outlined,
size: 16,
color: Colors.yellow.shade700,
),
),
const SizedBox(width: 12),
Text(
'Le mie Note',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: context.primaryText,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
const SizedBox(height: 12),
// Il corpo del widget collegato al Bloc
BlocBuilder<DashboardNoteListCubit, DashboardNoteListState>(
builder: (context, state) {
if (state.status == DashboardNoteListStatus.loading &&
state.notes.isEmpty) {
return const Center(child: CircularProgressIndicator());
}
if (state.status == DashboardNoteListStatus.failure) {
return const Center(
child: Text(
'Errore nel caricamento delle note.',
style: TextStyle(color: Colors.red),
),
);
}
if (state.notes.isEmpty) {
return _buildEmptyState(context);
}
// Prendiamo solo le prime 4 note per non intaccare troppo spazio in Dashboard
final displayNotes = state.notes.take(4).toList();
return SizedBox(
height: 140, // Altezza fissa per lo scroll orizzontale
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: displayNotes.length,
itemBuilder: (context, index) {
return _buildMiniPostIt(context, displayNotes[index]);
},
),
);
},
),
],
),
),
),
);
}
Widget _buildMiniPostIt(BuildContext context, NoteModel note) {
return GestureDetector(
onTap: () {
// Vai al form di dettaglio passando l'ID o l'oggetto
context.pushNamed(
Routes.noteForm,
pathParameters: {'id': note.id!},
extra: note,
);
},
child: Container(
width: 140,
margin: const EdgeInsets.only(right: 12, bottom: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: note.flutterColor,
borderRadius: BorderRadius.circular(8),
boxShadow: const [
BoxShadow(
color: Colors.black12,
blurRadius: 4,
offset: Offset(2, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
note.title?.isNotEmpty == true
? note.title!
: 'Senza titolo',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Colors.black87,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
if (note.isPinned)
const Icon(Icons.push_pin, size: 14, color: Colors.black54),
],
),
const SizedBox(height: 8),
Expanded(
child: Text(
note.content ?? '',
style: const TextStyle(fontSize: 12, color: Colors.black87),
maxLines: 4,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
);
}
Widget _buildEmptyState(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.grey.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Colors.grey.withValues(alpha: 0.3),
style: BorderStyle.solid,
),
),
child: Column(
children: [
const Icon(
Icons.sticky_note_2_outlined,
size: 32,
color: Colors.grey,
),
const SizedBox(height: 8),
const Text('Nessuna nota presente.'),
TextButton(
onPressed: () => context.push('/notes/create'),
child: const Text('Creane una ora'),
),
],
),
);
}
}

View File

@@ -6,8 +6,8 @@ import 'package:flux/features/home/dashboard_task_list/blocs/dashboard_task_list
import 'package:go_router/go_router.dart';
import 'package:flux/features/tasks/models/task_status.dart';
class DashboardTasksCard extends StatelessWidget {
const DashboardTasksCard({super.key});
class DashboardTaskListCard extends StatelessWidget {
const DashboardTaskListCard({super.key});
@override
Widget build(BuildContext context) {

View File

@@ -5,18 +5,19 @@ import 'package:flux/core/routes/routes.dart';
import 'package:flux/core/theme/theme.dart';
import 'package:flux/core/utils/extensions.dart';
import 'package:flux/core/widgets/staff_selector_modal.dart';
import 'package:flux/features/home/dashboard_note_list/blocs/dashboard_note_list_cubit.dart';
import 'package:flux/features/home/dashboard_store_operation_list/bloc/dashboard_store_operation_list_cubit.dart';
import 'package:flux/features/home/dashboard_store_ticket_list/blocs/dashboard_store_ticket_list_cubit.dart';
import 'package:flux/features/home/dashboard_store_ticket_list/ui/dashboard_store_ticket_list_card.dart';
import 'package:flux/features/home/dashboard_task_list/blocs/dashboard_task_list_cubit.dart';
import 'package:flux/features/home/dashboard_task_list/ui/dashboard_tasks_card.dart';
import 'package:flux/features/home/dashboard_task_list/ui/dashboard_task_list_card.dart';
import 'package:flux/features/home/dashboard_store_operation_list/ui/latest_store_operations_card.dart';
import 'package:flux/features/home/ui/quick_actions_widget.dart';
import 'package:flux/features/master_data/staff/blocs/staff_cubit.dart';
import 'package:flux/features/master_data/staff/models/staff_member_model.dart';
import 'package:flux/features/notes/data/notes_repository.dart';
import 'package:flux/features/notes/models/note_model.dart';
import 'package:flux/features/notes/ui/dashboard_notes_widget.dart';
import 'package:flux/features/home/dashboard_note_list/ui/dashboard_note_list_card.dart';
import 'package:get_it/get_it.dart';
import 'package:go_router/go_router.dart';
@@ -58,12 +59,14 @@ class _HomeScreenState extends State<HomeScreen> {
context.read<DashboardStoreOperationListCubit>().stopListening();
context.read<DashboardTaskListCubit>().stopListening();
context.read<DashboardStoreTicketListCubit>().stopListening();
context.read<DashboardNoteListCubit>().stopListening();
}
void _startListeners() {
context.read<DashboardStoreOperationListCubit>().startListening();
context.read<DashboardTaskListCubit>().startListening();
context.read<DashboardStoreTicketListCubit>().startListening();
context.read<DashboardNoteListCubit>().startListening();
}
@override
@@ -128,8 +131,8 @@ class _HomeScreenState extends State<HomeScreen> {
color: Colors.orange,
context: context,
),
DashboardNotesWidget(),
DashboardTasksCard(),
DashboardNoteListCard(),
DashboardTaskListCard(),
]),
),
),