UI Enhancements in IT Service Request, Announcements, Workforce and notification fixes
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math show min;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:skeletonizer/skeletonizer.dart';
|
||||
|
||||
import '../../models/it_service_request.dart';
|
||||
import '../../models/it_service_request_assignment.dart';
|
||||
@@ -13,6 +13,7 @@ import '../../providers/it_service_request_provider.dart';
|
||||
import '../../providers/profile_provider.dart';
|
||||
import '../../providers/realtime_controller.dart';
|
||||
import '../../providers/tickets_provider.dart';
|
||||
import '../../theme/m3_motion.dart';
|
||||
import '../../utils/app_time.dart';
|
||||
import '../../utils/snackbar.dart';
|
||||
import '../../widgets/m3_card.dart';
|
||||
@@ -100,27 +101,46 @@ class _ItServiceRequestsListScreenState
|
||||
children: [
|
||||
ResponsiveBody(
|
||||
maxWidth: double.infinity,
|
||||
child: Skeletonizer(
|
||||
enabled: showSkeleton,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
if (requestsAsync.hasError && !requestsAsync.hasValue) {
|
||||
return AppErrorView(
|
||||
error: requestsAsync.error!,
|
||||
onRetry: () =>
|
||||
ref.invalidate(itServiceRequestsProvider),
|
||||
);
|
||||
}
|
||||
final allRequests =
|
||||
requestsAsync.valueOrNull ?? <ItServiceRequest>[];
|
||||
if (allRequests.isEmpty && !showSkeleton) {
|
||||
return const AppEmptyView(
|
||||
icon: Icons.miscellaneous_services_outlined,
|
||||
title: 'No service requests yet',
|
||||
subtitle:
|
||||
'IT service requests submitted by your team will appear here.',
|
||||
);
|
||||
}
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
if (requestsAsync.hasError && !requestsAsync.hasValue) {
|
||||
return AppErrorView(
|
||||
error: requestsAsync.error!,
|
||||
onRetry: () =>
|
||||
ref.invalidate(itServiceRequestsProvider),
|
||||
);
|
||||
}
|
||||
if (showSkeleton) {
|
||||
return Column(
|
||||
children: [
|
||||
const AppPageHeader(
|
||||
title: 'IT Service Requests',
|
||||
subtitle: 'Manage and track IT support tickets',
|
||||
),
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16, vertical: 8),
|
||||
itemCount: 5,
|
||||
separatorBuilder: (_, _) =>
|
||||
const SizedBox(height: 8),
|
||||
itemBuilder: (_, _) =>
|
||||
_buildIsrShimmerCard(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
final allRequests =
|
||||
requestsAsync.valueOrNull ?? <ItServiceRequest>[];
|
||||
if (allRequests.isEmpty) {
|
||||
return const AppEmptyView(
|
||||
icon: Icons.miscellaneous_services_outlined,
|
||||
title: 'No service requests yet',
|
||||
subtitle:
|
||||
'IT service requests submitted by your team will appear here.',
|
||||
);
|
||||
}
|
||||
final offices = officesAsync.valueOrNull ?? <Office>[];
|
||||
final officesSorted = List<Office>.from(offices)
|
||||
..sort(
|
||||
@@ -278,13 +298,12 @@ class _ItServiceRequestsListScreenState
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
// FAB
|
||||
if (canCreate)
|
||||
Positioned(
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
child: FloatingActionButton.extended(
|
||||
child: M3ExpandedFab(
|
||||
heroTag: 'create_isr',
|
||||
onPressed: () => _showCreateDialog(context),
|
||||
icon: const Icon(Icons.add),
|
||||
@@ -416,6 +435,38 @@ class _ItServiceRequestsListScreenState
|
||||
if (context.mounted) showErrorSnackBar(context, 'Error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildIsrShimmerCard() {
|
||||
return M3Card.elevated(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
M3ShimmerBox(width: 80, height: 13),
|
||||
const Spacer(),
|
||||
M3ShimmerBox(width: 72, height: 22, borderRadius: BorderRadius.circular(11)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
M3ShimmerBox(height: 16),
|
||||
const SizedBox(height: 6),
|
||||
M3ShimmerBox(width: 200, height: 14),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
children: [
|
||||
M3ShimmerBox(width: 120, height: 12),
|
||||
const SizedBox(width: 16),
|
||||
M3ShimmerBox(width: 80, height: 12),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -561,9 +612,10 @@ class _RequestList extends StatelessWidget {
|
||||
if (requests.isEmpty) {
|
||||
return const Center(child: Text('No requests match the current filter.'));
|
||||
}
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
return ListView.separated(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
itemCount: requests.length,
|
||||
separatorBuilder: (_, _) => const SizedBox(height: 8),
|
||||
itemBuilder: (context, index) {
|
||||
final request = requests[index];
|
||||
final assignedStaff = assignments
|
||||
@@ -573,10 +625,13 @@ class _RequestList extends StatelessWidget {
|
||||
final office = request.officeId != null
|
||||
? officeById[request.officeId]?.name
|
||||
: null;
|
||||
return _RequestTile(
|
||||
request: request,
|
||||
officeName: office,
|
||||
assignedStaff: assignedStaff,
|
||||
return M3FadeSlideIn(
|
||||
delay: Duration(milliseconds: math.min(index, 8) * 50),
|
||||
child: _RequestTile(
|
||||
request: request,
|
||||
officeName: office,
|
||||
assignedStaff: assignedStaff,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user