Logs Refactoring Plan
SmartCrew Admin - Logs Refactoring Plan
Admin Panel Refactoring Plan
Overview
This document outlines the refactoring plan for the admin panel to support the new DailyLog architecture with standalone collections and optimized caching.
Current Architecture Issues
- Daily logs stored as subcollection under users collection
- Inefficient querying for admin reporting across multiple users
- No caching mechanism for frequently accessed admin data
- Performance issues when loading user logs in admin panel
Target Architecture
daily_logs (top-level collection)
├── documentId: "userId_date" (e.g., "user123_20231025")
│ ├── userId: string (reference to user document)
│ ├── companyId: string (reference to company document)
│ ├── logDate: timestamp
│ ├── status: string
│ ├── isDayOff: boolean
│ ├── dayOffDetails: map
│ ├── activities: subcollection
│ └── clockEvents: subcollectionRequired Changes
1. Update Data Fetching Methods
Current Implementation (to be changed):
final dailyLogs = await fireStore
.collection('users')
.doc(user.firebaseUid)
.collection('dailyLogs')
.get();New Implementation:
final dailyLogs = await fireStore
.collection('daily_logs')
.where('userId', isEqualTo: user.firebaseUid)
.where('companyId', isEqualTo: workspaceId)
.get();2. Implement Admin-Specific Caching
class AdminLogCache {
final Map<String, List<DailyLog>> _userLogsCache = {}; // Key: userId
final Map<String, Map<DateTime, DailyLog>> _dateRangeCache = {}; // Key: "startDate_endDate"
final Map<String, DateTime> _cacheTimestamps = {};
static const Duration userLogsCacheDuration = Duration(minutes: 30);
static const Duration dateRangeCacheDuration = Duration(hours: 2);
// Cache user logs
void cacheUserLogs(String userId, List<DailyLog> logs) {
_userLogsCache[userId] = logs;
_cacheTimestamps['user_$userId'] = DateTime.now();
}
// Get cached user logs
List<DailyLog>? getUserLogs(String userId) {
final key = 'user_$userId';
final timestamp = _cacheTimestamps[key];
if (timestamp == null ||
DateTime.now().difference(timestamp) > userLogsCacheDuration) {
return null;
}
return _userLogsCache[userId];
}
// Similar methods for date range caching
// ...
}3. Update User Log Fetching
Future<void> _fetchLogs(AppUser user, String workspaceId) async {
final List<DailyLog> userLogs = List.empty(growable: true);
// Check cache first
final cachedLogs = _adminCache.getUserLogs(user.firebaseUid);
if (cachedLogs != null) {
userLogs.addAll(cachedLogs);
_laborRepository.addUserLog(UserLogWrapper(user: user, logs: userLogs));
return;
}
// Fetch from new daily_logs collection
final querySnapshot = await fireStore.collection('daily_logs')
.where('userId', isEqualTo: user.firebaseUid)
.where('companyId', isEqualTo: workspaceId)
.get();
for (final doc in querySnapshot.docs) {
try {
final dailyLog = DailyLog.fromJson(doc.data());
userLogs.add(dailyLog);
} catch (e) {
NotificationsHelper().printIfDebugMode(
'Error parsing log: ${doc.id} for user: ${user.firebaseUid} with error: $e'
);
}
}
// Cache the results
_adminCache.cacheUserLogs(user.firebaseUid, userLogs);
_laborRepository.addUserLog(UserLogWrapper(user: user, logs: userLogs));
}4. Update Task Management Methods
Future<void> updateTaskStatus({
required String userId,
required DateTime logDate,
required int activityIndex,
required int taskIndex,
required TaskStatus newStatus,
}) async {
try {
// Generate composite ID
final compositeId = '${userId}_${DateFormat('yyyyMMdd').format(logDate)}';
// Get the document
final doc = await fireStore.collection('daily_logs').doc(compositeId).get();
if (!doc.exists) return;
// Process the update
final dailyLog = DailyLog.fromJson(doc.data()!);
final updatedLog = // ... update logic
// Save back to Firestore
await fireStore.collection('daily_logs').doc(compositeId).update({
'activities': updatedLog.activities.map((a) => a.toJson()).toList(),
});
// Update cache
_adminCache.updateUserLog(userId, logDate, updatedLog);
notifyListeners();
} catch (e) {
// Error handling
}
}5. Add Date Range Queries for Reporting
Future<List<DailyLog>> getLogsByDateRange(DateTime startDate, DateTime endDate, {String? companyId}) async {
final cacheKey = '${startDate.toIso8601String()}_${endDate.toIso8601String()}_$companyId';
// Check cache first
final cachedLogs = _adminCache.getDateRangeLogs(cacheKey);
if (cachedLogs != null) return cachedLogs;
// Build query
Query query = fireStore.collection('daily_logs')
.where('logDate', isGreaterThanOrEqualTo: Timestamp.fromDate(startDate))
.where('logDate', isLessThanOrEqualTo: Timestamp.fromDate(endDate));
if (companyId != null) {
query = query.where('companyId', isEqualTo: companyId);
}
// Execute query
final querySnapshot = await query.get();
final logs = querySnapshot.docs.map((doc) => DailyLog.fromJson(doc.data())).toList();
// Cache results
_adminCache.cacheDateRangeLogs(cacheKey, logs);
return logs;
}6. Update Weekly Submission Methods
Future<void> submitWeekWork(String userId, List<DailyLog> data, RequestStatus status) async {
final batch = fireStore.batch();
for (final log in data) {
if (log.companyId.isEmpty) continue;
final compositeId = '${userId}_${DateFormat('yyyyMMdd').format(log.logDate)}';
final docRef = fireStore.collection('daily_logs').doc(compositeId);
batch.update(docRef, {'status': status.name});
}
await batch.commit();
// Update cache
for (final log in data) {
final updatedLog = log.copyWith(status: status);
_adminCache.updateUserLog(userId, log.logDate, updatedLog);
}
_updateCurrentLogs(userId, data, status);
notifyListeners();
}Success Criteria
1. Performance Metrics
- Admin panel loads 50% faster when accessing user logs
- Date range queries complete in under 3 seconds for 30-day periods
- Task status updates reflect in UI within 1 second
2. Functional Requirements
- All admin functions work with new DailyLog structure
- Reporting features maintain or improve current functionality
- Real-time updates work correctly for admin actions
- Cache properly invalidates on data changes
3. Data Consistency
- All existing admin reports generate correctly
- User log access controls maintained
4. Error Handling
- Proper error handling for failed queries
- Graceful fallback when cache is unavailable
- Clear error messages for admin users
Implementation Checklist
Phase 1: Core Architecture Changes
- Update DailyLog model to include userId field
- Create composite ID generation utility
- Update all Firestore references to use new collection
Phase 2: Caching Implementation
- Create AdminLogCache class
- Implement user-specific caching
- Implement date-range caching for reports
- Add cache invalidation methods
Phase 3: Provider Refactoring
- Update _fetchLogs method to use new collection
- Update task management methods
- Update weekly submission methods
- Add date range query methods
Phase 4: UI Updates
- Ensure all UI components work with new data structure
- Add loading states for cache misses
- Update error handling displays
Phase 5: Testing
- Verify all admin functions work correctly
- Test cache hit/miss behavior
- Performance testing with large datasets
- Security testing for data access controls
Migration Strategy
- Phased Rollout: Update admin panel first
- Monitoring: Add detailed logging for cache performance and errors
This refactoring plan will ensure the admin panel works efficiently with the new DailyLog architecture while maintaining all existing functionality and providing better performance through strategic caching.