270 lines
8.3 KiB
Dart
270 lines
8.3 KiB
Dart
// lib/app/widgets/notification_card.dart
|
|
import 'package:flutter/material.dart';
|
|
import '../modules/notification/controllers/notification_controller.dart';
|
|
|
|
class NotificationCard extends StatelessWidget {
|
|
final NotificationItem notification;
|
|
final VoidCallback onDismissed;
|
|
final VoidCallback? onTap;
|
|
|
|
const NotificationCard({
|
|
super.key,
|
|
required this.notification,
|
|
required this.onDismissed,
|
|
this.onTap,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final bool isRead = notification.isRead;
|
|
|
|
final Color primaryColor =
|
|
isRead ? Colors.grey : const Color(0xFF0091EA);
|
|
|
|
final Color dangerColor =
|
|
isRead ? Colors.grey : const Color(0xFFE57373);
|
|
|
|
final Color successColor =
|
|
isRead ? Colors.grey : const Color(0xFF4CAF50);
|
|
|
|
return Dismissible(
|
|
key: Key(notification.id),
|
|
direction: DismissDirection.horizontal,
|
|
onDismissed: (_) => onDismissed(),
|
|
background: _buildDismissBackground(Alignment.centerLeft),
|
|
secondaryBackground: _buildDismissBackground(Alignment.centerRight),
|
|
child: InkWell(
|
|
onTap: isRead ? null : onTap,
|
|
child: Container(
|
|
margin: const EdgeInsets.only(bottom: 12),
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: isRead ? Colors.grey[50] : Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(
|
|
color: isRead ? Colors.grey[300]! : primaryColor.withOpacity(0.3),
|
|
width: isRead ? 1 : 2,
|
|
),
|
|
boxShadow: isRead
|
|
? null
|
|
: [
|
|
BoxShadow(
|
|
color: primaryColor.withOpacity(0.1),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
/// ICON
|
|
Container(
|
|
width: 40,
|
|
height: 40,
|
|
decoration: BoxDecoration(
|
|
color: isRead ? Colors.grey[200] : const Color(0xFFFFEBEE),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: Icon(
|
|
Icons.warning_rounded,
|
|
color: dangerColor,
|
|
size: 24,
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
|
|
/// CONTENT
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
/// TITLE + TIME
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
notification.title,
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: TextStyle(
|
|
fontSize: 15,
|
|
fontWeight: FontWeight.bold,
|
|
color: isRead
|
|
? Colors.grey[700]
|
|
: Colors.black,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
_formatTime(notification.time),
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: Colors.grey[500],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 6),
|
|
|
|
/// MESSAGE
|
|
Text(
|
|
notification.message,
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
color: isRead
|
|
? Colors.grey[600]
|
|
: Colors.grey[700],
|
|
height: 1.4,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
/// ROOM + DEVICE
|
|
Row(
|
|
children: [
|
|
_buildBadge(
|
|
text: notification.room,
|
|
bgColor: isRead
|
|
? Colors.grey[200]!
|
|
: const Color(0xFFE3F2FD),
|
|
textColor:
|
|
isRead ? Colors.grey : const Color(0xFF2196F3),
|
|
),
|
|
const SizedBox(width: 8),
|
|
_buildDeviceBadge(
|
|
notification.deviceId,
|
|
dotColor: successColor,
|
|
isRead: isRead,
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 6),
|
|
|
|
/// PATIENT NAME
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 12,
|
|
vertical: 6,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: isRead
|
|
? Colors.grey[200]
|
|
: const Color(0xFFE8F5E9),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Text(
|
|
notification.patientName,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w600,
|
|
color: isRead
|
|
? Colors.grey
|
|
: const Color(0xFF2E7D32),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// DISMISS BACKGROUND
|
|
Widget _buildDismissBackground(Alignment alignment) {
|
|
return Container(
|
|
margin: const EdgeInsets.only(bottom: 12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
alignment: alignment,
|
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
|
);
|
|
}
|
|
|
|
/// BADGE
|
|
Widget _buildBadge({
|
|
required String text,
|
|
required Color bgColor,
|
|
required Color textColor,
|
|
}) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
|
decoration: BoxDecoration(
|
|
color: bgColor,
|
|
borderRadius: BorderRadius.circular(6),
|
|
),
|
|
child: Text(
|
|
text,
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: textColor,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// DEVICE BADGE
|
|
Widget _buildDeviceBadge(
|
|
String deviceId, {
|
|
required Color dotColor,
|
|
required bool isRead,
|
|
}) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
|
decoration: BoxDecoration(
|
|
color: Colors.grey[100],
|
|
borderRadius: BorderRadius.circular(6),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Container(
|
|
width: 6,
|
|
height: 6,
|
|
decoration: BoxDecoration(
|
|
color: dotColor,
|
|
shape: BoxShape.circle,
|
|
),
|
|
),
|
|
const SizedBox(width: 6),
|
|
Text(
|
|
deviceId,
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: isRead ? Colors.grey : Colors.grey[700],
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// TIME FORMAT
|
|
String _formatTime(DateTime time) {
|
|
final diff = DateTime.now().difference(time);
|
|
|
|
if (diff.inMinutes < 60) {
|
|
return '${diff.inMinutes} min ago';
|
|
} else if (diff.inHours < 24) {
|
|
return '${diff.inHours} h ago';
|
|
} else {
|
|
return '${diff.inDays} d ago';
|
|
}
|
|
}
|
|
}
|