Initial commit — DLS lab-app Flutter project

This commit is contained in:
egecankomur
2026-06-10 23:22:15 +03:00
commit d1acc1d367
225 changed files with 31294 additions and 0 deletions
+195
View File
@@ -0,0 +1,195 @@
import 'package:flutter/widgets.dart' show Locale;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pocketbase/pocketbase.dart';
import '../auth/auth_repository.dart';
import '../services/notification_service.dart';
import '../../models/tenant.dart';
import '../../models/user_profile.dart';
import 'locale_provider.dart';
class AuthState {
const AuthState({
this.profile,
this.activeTenant,
this.memberships = const [],
this.isLoading = true,
this.error,
});
final UserProfile? profile;
final TenantMembership? activeTenant;
final List<TenantMembership> memberships;
final bool isLoading;
final String? error;
bool get isAuthenticated => profile != null;
AuthState copyWith({
UserProfile? profile,
TenantMembership? activeTenant,
List<TenantMembership>? memberships,
bool? isLoading,
String? error,
bool clearError = false,
}) =>
AuthState(
profile: profile ?? this.profile,
activeTenant: activeTenant ?? this.activeTenant,
memberships: memberships ?? this.memberships,
isLoading: isLoading ?? this.isLoading,
error: clearError ? null : (error ?? this.error),
);
}
class AuthNotifier extends StateNotifier<AuthState> {
AuthNotifier({this.onLocaleLoaded}) : super(const AuthState()) {
_init();
}
final void Function(String languageCode)? onLocaleLoaded;
final _repo = AuthRepository.instance;
Future<void> _init() async {
final loggedIn = await _repo.isLoggedIn();
if (!loggedIn) {
state = const AuthState(isLoading: false);
return;
}
try {
final result = await _repo.refreshSession();
state = AuthState(
profile: result.user,
memberships: result.tenants,
activeTenant:
result.tenants.isEmpty ? null : result.tenants.first,
isLoading: false,
);
final isLab = result.tenants.isNotEmpty && result.tenants.first.tenant.isLab;
NotificationService.loginUser(result.user.id, isLab: isLab);
_applyLocale(result.user.preferredLanguage);
} catch (_) {
state = const AuthState(isLoading: false);
}
}
void _applyLocale(String? code) {
if (code != null && code.isNotEmpty) {
onLocaleLoaded?.call(code);
}
}
Future<void> signIn(String email, String password) async {
state = state.copyWith(isLoading: true, clearError: true);
try {
final result = await _repo.login(email, password);
state = AuthState(
profile: result.user,
memberships: result.tenants,
activeTenant:
result.tenants.isEmpty ? null : result.tenants.first,
isLoading: false,
);
final isLab = result.tenants.isNotEmpty && result.tenants.first.tenant.isLab;
NotificationService.loginUser(result.user.id, isLab: isLab);
_applyLocale(result.user.preferredLanguage);
} catch (e) {
state = state.copyWith(isLoading: false, error: _parseError(e));
}
}
Future<void> register({
required String email,
required String password,
String? firstName,
String? lastName,
}) async {
state = state.copyWith(isLoading: true, clearError: true);
try {
final result = await _repo.register(
email: email,
password: password,
firstName: firstName,
lastName: lastName,
);
state = AuthState(
profile: result.user,
memberships: result.tenants,
activeTenant:
result.tenants.isEmpty ? null : result.tenants.first,
isLoading: false,
);
} catch (e) {
state = state.copyWith(isLoading: false, error: _parseError(e));
rethrow;
}
}
Future<void> signOut() async {
await _repo.logout();
await NotificationService.logoutUser();
state = const AuthState(isLoading: false);
}
void setActiveTenant(TenantMembership membership) {
state = state.copyWith(activeTenant: membership);
}
Future<void> refresh() async {
try {
final result = await _repo.refreshSession();
final currentId = state.activeTenant?.tenant.id;
final newActive = currentId != null
? result.tenants.firstWhere(
(m) => m.tenant.id == currentId,
orElse: () => result.tenants.isNotEmpty
? result.tenants.first
: state.activeTenant!,
)
: (result.tenants.isNotEmpty ? result.tenants.first : null);
state = state.copyWith(
profile: result.user,
memberships: result.tenants,
activeTenant: newActive,
);
} catch (_) {}
}
Future<void> updateLanguage(String languageCode) async {
final userId = state.profile?.id;
if (userId == null) return;
await _repo.updateUserLanguage(userId, languageCode);
}
Future<void> updateTenantInfo({
required String tenantId,
required String companyName,
String? defaultCurrency,
}) async {
await _repo.updateTenant(
tenantId,
companyName: companyName,
defaultCurrency: defaultCurrency,
);
await refresh();
}
String _parseError(Object e) {
if (e is ClientException) {
final code = e.statusCode;
if (code == 400 || code == 401 || code == 403) {
return 'E-posta veya şifre hatalı.';
}
final msg = e.response['message'] as String? ?? '';
if (msg.isNotEmpty) return msg;
}
return 'Bağlantı hatası. Lütfen tekrar deneyin.';
}
}
final authProvider =
StateNotifierProvider<AuthNotifier, AuthState>((ref) {
return AuthNotifier(
onLocaleLoaded: (code) =>
ref.read(localeProvider.notifier).setLocale(Locale(code)),
);
});