Initial commit: TimeReg Flutter app med Firebase backend
- 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
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user