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:
steinhelge
2025-11-24 20:52:27 +01:00
commit c829f78984
148 changed files with 8462 additions and 0 deletions
+195
View File
@@ -0,0 +1,195 @@
import 'package:cloud_firestore/cloud_firestore.dart';
class NightWorkRules {
final int startHour; // e.g., 21 for 9 PM
final int endHour; // e.g., 6 for 6 AM
final int maxNightHours;
NightWorkRules({
required this.startHour,
required this.endHour,
required this.maxNightHours,
});
factory NightWorkRules.fromMap(Map<String, dynamic> map) {
return NightWorkRules(
startHour: map['startHour'] ?? 21,
endHour: map['endHour'] ?? 6,
maxNightHours: map['maxNightHours'] ?? 0,
);
}
Map<String, dynamic> toMap() {
return {
'startHour': startHour,
'endHour': endHour,
'maxNightHours': maxNightHours,
};
}
}
class TariffRules {
final int maxDailyHours;
final int maxWeeklyHours;
final int minDailyRest; // in minutes
final int minWeeklyRest; // in minutes
final bool useAverageCalculation;
final int? averagePeriodWeeks;
final int? maxAverageHours;
final NightWorkRules? nightWorkRules;
TariffRules({
required this.maxDailyHours,
required this.maxWeeklyHours,
required this.minDailyRest,
required this.minWeeklyRest,
this.useAverageCalculation = false,
this.averagePeriodWeeks,
this.maxAverageHours,
this.nightWorkRules,
});
factory TariffRules.fromMap(Map<String, dynamic> map) {
return TariffRules(
maxDailyHours: map['maxDailyHours'] ?? 9,
maxWeeklyHours: map['maxWeeklyHours'] ?? 40,
minDailyRest: map['minDailyRest'] ?? 660, // 11 hours
minWeeklyRest: map['minWeeklyRest'] ?? 2100, // 35 hours
useAverageCalculation: map['useAverageCalculation'] ?? false,
averagePeriodWeeks: map['averagePeriodWeeks'],
maxAverageHours: map['maxAverageHours'],
nightWorkRules: map['nightWorkRules'] != null
? NightWorkRules.fromMap(map['nightWorkRules'])
: null,
);
}
Map<String, dynamic> toMap() {
return {
'maxDailyHours': maxDailyHours,
'maxWeeklyHours': maxWeeklyHours,
'minDailyRest': minDailyRest,
'minWeeklyRest': minWeeklyRest,
'useAverageCalculation': useAverageCalculation,
'averagePeriodWeeks': averagePeriodWeeks,
'maxAverageHours': maxAverageHours,
'nightWorkRules': nightWorkRules?.toMap(),
};
}
}
class TariffProfile {
final String id;
final String name;
final String description;
final String organizationId;
final TariffRules rules;
final bool isDefault;
final bool isActive;
final DateTime createdAt;
final DateTime updatedAt;
TariffProfile({
required this.id,
required this.name,
required this.description,
required this.organizationId,
required this.rules,
this.isDefault = false,
this.isActive = true,
required this.createdAt,
required this.updatedAt,
});
factory TariffProfile.fromFirestore(DocumentSnapshot doc) {
final data = doc.data() as Map<String, dynamic>;
return TariffProfile(
id: doc.id,
name: data['name'] ?? '',
description: data['description'] ?? '',
organizationId: data['organizationId'] ?? '',
rules: TariffRules.fromMap(data['rules'] ?? {}),
isDefault: data['isDefault'] ?? false,
isActive: data['isActive'] ?? true,
createdAt: (data['createdAt'] as Timestamp).toDate(),
updatedAt: (data['updatedAt'] as Timestamp).toDate(),
);
}
Map<String, dynamic> toFirestore() {
return {
'name': name,
'description': description,
'organizationId': organizationId,
'rules': rules.toMap(),
'isDefault': isDefault,
'isActive': isActive,
'createdAt': Timestamp.fromDate(createdAt),
'updatedAt': Timestamp.fromDate(updatedAt),
};
}
TariffProfile copyWith({
String? name,
String? description,
String? organizationId,
TariffRules? rules,
bool? isDefault,
bool? isActive,
DateTime? updatedAt,
}) {
return TariffProfile(
id: id,
name: name ?? this.name,
description: description ?? this.description,
organizationId: organizationId ?? this.organizationId,
rules: rules ?? this.rules,
isDefault: isDefault ?? this.isDefault,
isActive: isActive ?? this.isActive,
createdAt: createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
// Predefined profiles
static TariffProfile createDefaultAML(String organizationId) {
return TariffProfile(
id: 'default_aml',
name: 'Ingen tariffavtale (AML Standard)',
description: 'Standard arbeidsmiljøloven uten tariffavtale',
organizationId: organizationId,
rules: TariffRules(
maxDailyHours: 9,
maxWeeklyHours: 40,
minDailyRest: 660, // 11 hours
minWeeklyRest: 2100, // 35 hours
),
isDefault: true,
isActive: true,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
}
static TariffProfile createTariffA(String organizationId) {
return TariffProfile(
id: 'tariff_a',
name: 'Tariffavtale A',
description: 'Tariffavtale med utvidet arbeidstid og gjennomsnittsberegning',
organizationId: organizationId,
rules: TariffRules(
maxDailyHours: 10,
maxWeeklyHours: 48,
minDailyRest: 660, // 11 hours
minWeeklyRest: 2100, // 35 hours
useAverageCalculation: true,
averagePeriodWeeks: 8,
maxAverageHours: 48,
),
isDefault: false,
isActive: true,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
}
}