-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
344 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// lib/domain/events.dart | ||
|
||
import 'package:intl/intl.dart'; | ||
|
||
class Event { | ||
final String id; // Document ID in Firestore | ||
final String title; | ||
final String content; | ||
final String organizer; | ||
final String participants; | ||
final String location; | ||
final DateTime date; | ||
|
||
Event({ | ||
required this.id, | ||
required this.title, | ||
required this.content, | ||
required this.organizer, | ||
required this.participants, | ||
required this.location, | ||
required this.date, | ||
}); | ||
|
||
/// Factory method để tạo Event từ tài liệu Firestore | ||
factory Event.fromFirestore(Map<String, dynamic> data, String documentId) { | ||
try { | ||
String? timestamp = data['date']?['timestampValue']; | ||
DateTime parsedDate = timestamp != null | ||
? DateTime.parse(timestamp).toLocal() // Chuyển đổi sang múi giờ địa phương | ||
: DateTime.now(); | ||
|
||
return Event( | ||
id: documentId, | ||
title: data['title']?['stringValue'] ?? '', | ||
content: data['content']?['stringValue'] ?? '', | ||
organizer: data['organizer']?['stringValue'] ?? '', | ||
participants: data['participants']?['stringValue'] ?? '', | ||
location: data['location']?['stringValue'] ?? '', | ||
date: parsedDate, | ||
); | ||
} catch (e) { | ||
return Event( | ||
id: documentId, | ||
title: 'Error', | ||
content: '', | ||
organizer: '', | ||
participants: '', | ||
location: '', | ||
date: DateTime.now(), | ||
); | ||
} | ||
} | ||
|
||
/// Chuyển đổi Event thành các trường Firestore | ||
Map<String, dynamic> toFirestore() { | ||
return { | ||
'title': {'stringValue': title}, | ||
'content': {'stringValue': content}, | ||
'organizer': {'stringValue': organizer}, | ||
'participants': {'stringValue': participants}, | ||
'location': {'stringValue': location}, | ||
'date': {'timestampValue': date.toUtc().toIso8601String()}, | ||
}; | ||
} | ||
} | ||
|
||
class EventWithDate { | ||
final Event event; | ||
final DateTime date; | ||
|
||
EventWithDate({required this.event, required this.date}); | ||
} |
114 changes: 114 additions & 0 deletions
114
lib/features/admin/presentation/widgets/daily_event_box.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
// lib/widgets/daily_event_box.dart | ||
|
||
import 'package:flutter/material.dart'; | ||
import 'package:intl/intl.dart'; | ||
import '../../domain/events.dart'; | ||
|
||
class DailyEventBox extends StatelessWidget { | ||
final DateTime selectedDay; | ||
final List<Event> events; | ||
final Function(Event) onEdit; | ||
final Function(Event) onDelete; | ||
|
||
const DailyEventBox({ | ||
Key? key, | ||
required this.selectedDay, | ||
required this.events, | ||
required this.onEdit, | ||
required this.onDelete, | ||
}) : super(key: key); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
String formattedDate = DateFormat('dd/MM/yyyy').format(selectedDay); | ||
|
||
return Container( | ||
decoration: BoxDecoration( | ||
color: Colors.blue[50], // Màu nền xanh dương nhẹ | ||
borderRadius: BorderRadius.circular(10), | ||
boxShadow: [ | ||
BoxShadow( | ||
color: Colors.grey.withOpacity(0.3), | ||
spreadRadius: 2, | ||
blurRadius: 5, | ||
offset: const Offset(0, 3), | ||
), | ||
], | ||
), | ||
padding: const EdgeInsets.all(16.0), | ||
width: double.infinity, // Ensure full width | ||
child: Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: [ | ||
// Tiêu đề | ||
Text( | ||
'Sự kiện ngày $formattedDate', | ||
style: const TextStyle( | ||
fontSize: 18, | ||
fontWeight: FontWeight.bold, | ||
), | ||
), | ||
const SizedBox(height: 8.0), | ||
// Danh sách sự kiện | ||
events.isEmpty | ||
? const Center( | ||
child: Text('Không có sự kiện nào trong ngày này.'), | ||
) | ||
: ListView.builder( | ||
shrinkWrap: true, | ||
physics: const BouncingScrollPhysics(), | ||
itemCount: events.length, | ||
itemBuilder: (context, index) { | ||
final event = events[index]; | ||
return ExpansionTile( | ||
leading: const Icon(Icons.event), | ||
title: Row( | ||
children: [ | ||
Expanded( | ||
child: Text( | ||
event.title, | ||
overflow: TextOverflow.ellipsis, | ||
maxLines: 1, | ||
), | ||
), | ||
IconButton( | ||
icon: const Icon(Icons.edit, color: Colors.blue), | ||
onPressed: () { | ||
onEdit(event); | ||
}, | ||
), | ||
IconButton( | ||
icon: const Icon(Icons.delete, color: Colors.red), | ||
onPressed: () { | ||
onDelete(event); | ||
}, | ||
), | ||
], | ||
), | ||
children: [ | ||
Padding( | ||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), | ||
child: Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: [ | ||
Text('Nội dung: ${event.content}'), | ||
const SizedBox(height: 4), | ||
Text('Người tổ chức: ${event.organizer}'), | ||
const SizedBox(height: 4), | ||
Text('Thành phần tham dự: ${event.participants}'), | ||
const SizedBox(height: 4), | ||
Text('Địa điểm: ${event.location}'), | ||
const SizedBox(height: 4), | ||
Text('Thời gian: ${DateFormat('dd/MM/yyyy').format(event.date)}'), | ||
], | ||
), | ||
), | ||
], | ||
); | ||
}, | ||
), | ||
], | ||
), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
// lib/widgets/event_box.dart | ||
|
||
import 'package:flutter/material.dart'; | ||
import 'package:intl/intl.dart'; | ||
import '../../domain/events.dart'; | ||
|
||
class EventBox extends StatelessWidget { | ||
final String title; | ||
final int count; | ||
final List<EventWithDate> events; | ||
final String emptyMessage; | ||
final Function(Event) onEventTap; | ||
final Function(Event) onEdit; | ||
final Function(Event) onDelete; | ||
final String screenSize; // 'wide', 'medium', 'small' | ||
|
||
const EventBox({ | ||
Key? key, | ||
required this.title, | ||
required this.count, | ||
required this.events, | ||
required this.emptyMessage, | ||
required this.onEventTap, | ||
required this.onEdit, | ||
required this.onDelete, | ||
required this.screenSize, | ||
}) : super(key: key); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
// Determine button layout based on screen size | ||
bool isMediumScreen = screenSize == 'medium'; | ||
bool isWideScreen = screenSize == 'wide'; | ||
bool isSmallScreen = screenSize == 'small'; | ||
|
||
return Container( | ||
decoration: BoxDecoration( | ||
color: Colors.white, // Màu nền trắng | ||
borderRadius: BorderRadius.circular(10), | ||
boxShadow: [ | ||
BoxShadow( | ||
color: Colors.grey.withOpacity(0.3), | ||
spreadRadius: 2, | ||
blurRadius: 5, | ||
offset: const Offset(0, 3), | ||
), | ||
], | ||
), | ||
padding: const EdgeInsets.all(16.0), | ||
width: double.infinity, // Ensure full width | ||
child: Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: [ | ||
// Tiêu đề và thống kê | ||
Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: [ | ||
Text( | ||
title, | ||
style: const TextStyle( | ||
fontSize: 18, | ||
fontWeight: FontWeight.bold, | ||
), | ||
), | ||
const SizedBox(height: 4.0), | ||
Text( | ||
count > 0 ? (title == 'Sự kiện sắp tới' ? 'Trong 30 ngày sắp tới: $count sự kiện' : 'Trong 30 ngày đã qua: $count sự kiện') : (title == 'Sự kiện sắp tới' ? 'Trong 30 ngày sắp tới: 0 sự kiện' : 'Trong 30 ngày đã qua: 0 sự kiện'), | ||
style: const TextStyle( | ||
fontSize: 14, | ||
color: Colors.grey, | ||
), | ||
), | ||
], | ||
), | ||
const SizedBox(height: 8.0), | ||
// Danh sách sự kiện | ||
events.isEmpty | ||
? Padding( | ||
padding: const EdgeInsets.symmetric(horizontal: 8.0), | ||
child: Text(emptyMessage), | ||
) | ||
: ListView.builder( | ||
shrinkWrap: true, | ||
physics: const NeverScrollableScrollPhysics(), | ||
itemCount: events.length, | ||
itemBuilder: (context, index) { | ||
final eventWithDate = events[index]; | ||
return ListTile( | ||
leading: const Icon(Icons.event), | ||
title: isMediumScreen || isSmallScreen | ||
? Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: [ | ||
Text( | ||
eventWithDate.event.title, | ||
overflow: TextOverflow.ellipsis, | ||
maxLines: 1, | ||
), | ||
const SizedBox(height: 4.0), | ||
Row( | ||
children: [ | ||
IconButton( | ||
icon: const Icon(Icons.edit, color: Colors.blue, size: 20), | ||
onPressed: () { | ||
onEdit(eventWithDate.event); | ||
}, | ||
), | ||
IconButton( | ||
icon: const Icon(Icons.delete, color: Colors.red, size: 20), | ||
onPressed: () { | ||
onDelete(eventWithDate.event); | ||
}, | ||
), | ||
], | ||
), | ||
], | ||
) | ||
: Row( | ||
children: [ | ||
Expanded( | ||
child: Text( | ||
eventWithDate.event.title, | ||
overflow: TextOverflow.ellipsis, | ||
maxLines: 1, | ||
), | ||
), | ||
IconButton( | ||
icon: const Icon(Icons.edit, color: Colors.blue), | ||
onPressed: () { | ||
onEdit(eventWithDate.event); | ||
}, | ||
), | ||
IconButton( | ||
icon: const Icon(Icons.delete, color: Colors.red), | ||
onPressed: () { | ||
onDelete(eventWithDate.event); | ||
}, | ||
), | ||
], | ||
), | ||
subtitle: Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: [ | ||
Text('Thời gian: ${DateFormat('dd/MM/yyyy').format(eventWithDate.date)}'), | ||
Text('Địa điểm: ${eventWithDate.event.location}'), | ||
], | ||
), | ||
onTap: () { | ||
onEventTap(eventWithDate.event); | ||
}, | ||
); | ||
}, | ||
), | ||
], | ||
), | ||
); | ||
} | ||
} |