c829f78984
- Opprettet Flutter-prosjekt med alle nødvendige avhengigheter - Implementert datamodeller (User, TimeRegistration, TariffProfile, Deviation, AuditLog) - Implementert tjenester (AuthService, TimeService) - Implementert Riverpod providers for state management - Opprettet autentiseringsskjermer (login, signup, reset password) - Opprettet hjemmeskjerm med timer-funksjonalitet - Opprettet placeholder-skjermer for historikk, rapporter og profil - Lagt til norsk dokumentasjon i README
211 lines
5.6 KiB
Dart
211 lines
5.6 KiB
Dart
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
|
|
enum RegistrationType {
|
|
ordinary,
|
|
overtime,
|
|
oncall,
|
|
travel,
|
|
}
|
|
|
|
enum SyncStatus {
|
|
synced,
|
|
pending,
|
|
conflict,
|
|
}
|
|
|
|
class TimeBreak {
|
|
final DateTime startTime;
|
|
final DateTime endTime;
|
|
final int duration; // in minutes
|
|
|
|
TimeBreak({
|
|
required this.startTime,
|
|
required this.endTime,
|
|
required this.duration,
|
|
});
|
|
|
|
factory TimeBreak.fromMap(Map<String, dynamic> map) {
|
|
return TimeBreak(
|
|
startTime: (map['startTime'] as Timestamp).toDate(),
|
|
endTime: (map['endTime'] as Timestamp).toDate(),
|
|
duration: map['duration'] ?? 0,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toMap() {
|
|
return {
|
|
'startTime': Timestamp.fromDate(startTime),
|
|
'endTime': Timestamp.fromDate(endTime),
|
|
'duration': duration,
|
|
};
|
|
}
|
|
}
|
|
|
|
class TimeRegistration {
|
|
final String id;
|
|
final String userId;
|
|
final String organizationId;
|
|
final DateTime startTime;
|
|
final DateTime? endTime;
|
|
final int duration; // in minutes
|
|
final RegistrationType type;
|
|
final String? projectId;
|
|
final String? customerId;
|
|
final String? comment;
|
|
final List<TimeBreak> breaks;
|
|
final DateTime createdAt;
|
|
final DateTime updatedAt;
|
|
final String createdBy;
|
|
final String? lastModifiedBy;
|
|
final List<String> deviations;
|
|
final bool isManual;
|
|
final SyncStatus syncStatus;
|
|
|
|
TimeRegistration({
|
|
required this.id,
|
|
required this.userId,
|
|
required this.organizationId,
|
|
required this.startTime,
|
|
this.endTime,
|
|
required this.duration,
|
|
this.type = RegistrationType.ordinary,
|
|
this.projectId,
|
|
this.customerId,
|
|
this.comment,
|
|
this.breaks = const [],
|
|
required this.createdAt,
|
|
required this.updatedAt,
|
|
required this.createdBy,
|
|
this.lastModifiedBy,
|
|
this.deviations = const [],
|
|
this.isManual = false,
|
|
this.syncStatus = SyncStatus.synced,
|
|
});
|
|
|
|
factory TimeRegistration.fromFirestore(DocumentSnapshot doc) {
|
|
final data = doc.data() as Map<String, dynamic>;
|
|
return TimeRegistration(
|
|
id: doc.id,
|
|
userId: data['userId'] ?? '',
|
|
organizationId: data['organizationId'] ?? '',
|
|
startTime: (data['startTime'] as Timestamp).toDate(),
|
|
endTime: data['endTime'] != null
|
|
? (data['endTime'] as Timestamp).toDate()
|
|
: null,
|
|
duration: data['duration'] ?? 0,
|
|
type: _parseType(data['type']),
|
|
projectId: data['projectId'],
|
|
customerId: data['customerId'],
|
|
comment: data['comment'],
|
|
breaks: (data['breaks'] as List<dynamic>?)
|
|
?.map((b) => TimeBreak.fromMap(b as Map<String, dynamic>))
|
|
.toList() ??
|
|
[],
|
|
createdAt: (data['createdAt'] as Timestamp).toDate(),
|
|
updatedAt: (data['updatedAt'] as Timestamp).toDate(),
|
|
createdBy: data['createdBy'] ?? '',
|
|
lastModifiedBy: data['lastModifiedBy'],
|
|
deviations: List<String>.from(data['deviations'] ?? []),
|
|
isManual: data['isManual'] ?? false,
|
|
syncStatus: _parseSyncStatus(data['syncStatus']),
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toFirestore() {
|
|
return {
|
|
'userId': userId,
|
|
'organizationId': organizationId,
|
|
'startTime': Timestamp.fromDate(startTime),
|
|
'endTime': endTime != null ? Timestamp.fromDate(endTime!) : null,
|
|
'duration': duration,
|
|
'type': type.name,
|
|
'projectId': projectId,
|
|
'customerId': customerId,
|
|
'comment': comment,
|
|
'breaks': breaks.map((b) => b.toMap()).toList(),
|
|
'createdAt': Timestamp.fromDate(createdAt),
|
|
'updatedAt': Timestamp.fromDate(updatedAt),
|
|
'createdBy': createdBy,
|
|
'lastModifiedBy': lastModifiedBy,
|
|
'deviations': deviations,
|
|
'isManual': isManual,
|
|
'syncStatus': syncStatus.name,
|
|
};
|
|
}
|
|
|
|
static RegistrationType _parseType(String? typeString) {
|
|
switch (typeString) {
|
|
case 'overtime':
|
|
return RegistrationType.overtime;
|
|
case 'oncall':
|
|
return RegistrationType.oncall;
|
|
case 'travel':
|
|
return RegistrationType.travel;
|
|
default:
|
|
return RegistrationType.ordinary;
|
|
}
|
|
}
|
|
|
|
static SyncStatus _parseSyncStatus(String? statusString) {
|
|
switch (statusString) {
|
|
case 'pending':
|
|
return SyncStatus.pending;
|
|
case 'conflict':
|
|
return SyncStatus.conflict;
|
|
default:
|
|
return SyncStatus.synced;
|
|
}
|
|
}
|
|
|
|
int get totalBreakMinutes {
|
|
return breaks.fold(0, (sum, b) => sum + b.duration);
|
|
}
|
|
|
|
int get netWorkMinutes {
|
|
return duration - totalBreakMinutes;
|
|
}
|
|
|
|
bool get isActive {
|
|
return endTime == null;
|
|
}
|
|
|
|
TimeRegistration copyWith({
|
|
String? userId,
|
|
String? organizationId,
|
|
DateTime? startTime,
|
|
DateTime? endTime,
|
|
int? duration,
|
|
RegistrationType? type,
|
|
String? projectId,
|
|
String? customerId,
|
|
String? comment,
|
|
List<TimeBreak>? breaks,
|
|
DateTime? updatedAt,
|
|
String? lastModifiedBy,
|
|
List<String>? deviations,
|
|
bool? isManual,
|
|
SyncStatus? syncStatus,
|
|
}) {
|
|
return TimeRegistration(
|
|
id: id,
|
|
userId: userId ?? this.userId,
|
|
organizationId: organizationId ?? this.organizationId,
|
|
startTime: startTime ?? this.startTime,
|
|
endTime: endTime ?? this.endTime,
|
|
duration: duration ?? this.duration,
|
|
type: type ?? this.type,
|
|
projectId: projectId ?? this.projectId,
|
|
customerId: customerId ?? this.customerId,
|
|
comment: comment ?? this.comment,
|
|
breaks: breaks ?? this.breaks,
|
|
createdAt: createdAt,
|
|
updatedAt: updatedAt ?? this.updatedAt,
|
|
createdBy: createdBy,
|
|
lastModifiedBy: lastModifiedBy ?? this.lastModifiedBy,
|
|
deviations: deviations ?? this.deviations,
|
|
isManual: isManual ?? this.isManual,
|
|
syncStatus: syncStatus ?? this.syncStatus,
|
|
);
|
|
}
|
|
}
|