chore: normalize line endings (CRLF -> LF)

No content changes: git diff --ignore-all-space over these files is empty.
The churn came from editing on Windows against a repo checked out with LF.
This commit is contained in:
fhanyuh committed 2026-08-27 10:40:49 +07:00
1 parent 15566a6951
commit caf8e98378
315 files changed
+86950 -86950

No files matched your search

+99 -99
View File
@@ -1,99 +1,99 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../core/network/api_client.dart';
import '../../config/app_config.dart';
import '../../main.dart';
import '../documents/pending_documents_provider.dart';
class AuthNotifier extends StateNotifier<bool> {
AuthNotifier(this._ref) : super(false);
final Ref _ref;
Future<bool> checkLoginState() async {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('auth_token');
if (token != null) {
state = true;
try {
final apiClient = _ref.read(apiClientProvider);
final response = await apiClient.client.get('/auth/me');
if (response.data is Map && response.data['data'] is Map) {
final profile = response.data['data']['profile'];
if (profile != null) {
await prefs.setString('nama_toko', profile['namaToko'] ?? '');
await prefs.setString('kode_toko', profile['kodeToko'] ?? '');
}
}
} catch (e) {
// If unauthorized, perform clean logout
if (e.toString().contains('401') || e.toString().contains('Unauthorized')) {
await logout();
return false;
}
}
return true;
}
state = false;
return false;
}
Future<void> login(String username, String password) async {
final apiClient = _ref.read(apiClientProvider);
try {
final response = await apiClient.client.post(
AppConfig.loginEndpoint,
data: {
'username': username,
'password': password,
},
);
if (response.data is! Map || response.data['data'] is! Map) {
throw Exception("Format respon server tidak valid (bukan JSON)");
}
final token = response.data['data']['token'];
final profile = response.data['data']['profile'];
if (token == null) {
throw Exception("Token otentikasi tidak ditemukan dalam respon");
}
// Clear any pre-existing local data before saving the new token/profile
final localStorage = _ref.read(localStorageProvider);
await localStorage.clearAll();
_ref.read(pendingDocumentsProvider.notifier).clearQueue();
final prefs = await SharedPreferences.getInstance();
await prefs.setString('auth_token', token);
if (profile != null) {
await prefs.setString('nama_toko', profile['namaToko'] ?? '');
await prefs.setString('kode_toko', profile['kodeToko'] ?? '');
}
state = true;
} catch (e) {
state = false;
rethrow;
}
}
Future<void> logout() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('auth_token');
await prefs.remove('nama_toko');
await prefs.remove('kode_toko');
final localStorage = _ref.read(localStorageProvider);
await localStorage.clearAll();
// Clear pending documents queue in memory
_ref.read(pendingDocumentsProvider.notifier).clearQueue();
state = false;
}
}
final authProvider = StateNotifierProvider<AuthNotifier, bool>((ref) {
return AuthNotifier(ref);
});
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../core/network/api_client.dart';
import '../../config/app_config.dart';
import '../../main.dart';
import '../documents/pending_documents_provider.dart';
class AuthNotifier extends StateNotifier<bool> {
AuthNotifier(this._ref) : super(false);
final Ref _ref;
Future<bool> checkLoginState() async {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('auth_token');
if (token != null) {
state = true;
try {
final apiClient = _ref.read(apiClientProvider);
final response = await apiClient.client.get('/auth/me');
if (response.data is Map && response.data['data'] is Map) {
final profile = response.data['data']['profile'];
if (profile != null) {
await prefs.setString('nama_toko', profile['namaToko'] ?? '');
await prefs.setString('kode_toko', profile['kodeToko'] ?? '');
}
}
} catch (e) {
// If unauthorized, perform clean logout
if (e.toString().contains('401') || e.toString().contains('Unauthorized')) {
await logout();
return false;
}
}
return true;
}
state = false;
return false;
}
Future<void> login(String username, String password) async {
final apiClient = _ref.read(apiClientProvider);
try {
final response = await apiClient.client.post(
AppConfig.loginEndpoint,
data: {
'username': username,
'password': password,
},
);
if (response.data is! Map || response.data['data'] is! Map) {
throw Exception("Format respon server tidak valid (bukan JSON)");
}
final token = response.data['data']['token'];
final profile = response.data['data']['profile'];
if (token == null) {
throw Exception("Token otentikasi tidak ditemukan dalam respon");
}
// Clear any pre-existing local data before saving the new token/profile
final localStorage = _ref.read(localStorageProvider);
await localStorage.clearAll();
_ref.read(pendingDocumentsProvider.notifier).clearQueue();
final prefs = await SharedPreferences.getInstance();
await prefs.setString('auth_token', token);
if (profile != null) {
await prefs.setString('nama_toko', profile['namaToko'] ?? '');
await prefs.setString('kode_toko', profile['kodeToko'] ?? '');
}
state = true;
} catch (e) {
state = false;
rethrow;
}
}
Future<void> logout() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('auth_token');
await prefs.remove('nama_toko');
await prefs.remove('kode_toko');
final localStorage = _ref.read(localStorageProvider);
await localStorage.clearAll();
// Clear pending documents queue in memory
_ref.read(pendingDocumentsProvider.notifier).clearQueue();
state = false;
}
}
final authProvider = StateNotifierProvider<AuthNotifier, bool>((ref) {
return AuthNotifier(ref);
});
+151 -151
View File
@@ -1,151 +1,151 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../config/app_config.dart';
import '../../core/network/api_exception.dart';
import 'auth_provider.dart';
class LoginScreen extends ConsumerStatefulWidget {
const LoginScreen({super.key});
@override
ConsumerState<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends ConsumerState<LoginScreen> {
final _idController = TextEditingController();
final _passwordController = TextEditingController();
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
void _handleLogin() async {
if (!_formKey.currentState!.validate()) return;
final id = _idController.text.trim();
final pwd = _passwordController.text.trim();
setState(() => _isLoading = true);
try {
await ref.read(authProvider.notifier).login(id, pwd);
if (mounted) {
context.go('/camera');
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
ApiException.from(e).displayMessage,
style: const TextStyle(color: Colors.white),
),
backgroundColor: AppConfig.errorColor,
),
);
}
} finally {
if (mounted) {
setState(() => _isLoading = false);
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(
horizontal: AppConfig.spaceXXL,
vertical: AppConfig.spaceXL,
),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Image.asset(
'assets/logo.png',
width: 120,
height: 120,
errorBuilder: (context, error, stackTrace) => const Icon(
Icons.storefront,
size: 80,
color: AppConfig.primaryColor
),
),
const SizedBox(height: AppConfig.spaceXL),
Text(
'Selamat Datang Kembali',
style: AppConfig.h1,
textAlign: TextAlign.center,
),
const SizedBox(height: AppConfig.spaceSmall),
Text(
'Masuk untuk menyinkronkan dokumen Anda ke server pusat',
style: AppConfig.bodyMedium,
textAlign: TextAlign.center,
),
const SizedBox(height: AppConfig.spaceXXL * 1.5),
TextFormField(
controller: _idController,
decoration: const InputDecoration(
labelText: 'ID dari Admin',
prefixIcon: Icon(Icons.person_outline),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Username/ID tidak boleh kosong';
}
return null;
},
),
const SizedBox(height: AppConfig.spaceLarge),
TextFormField(
controller: _passwordController,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Kata Sandi',
prefixIcon: Icon(Icons.lock_outline),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Kata sandi tidak boleh kosong';
}
if (value.trim().length < 6) {
return 'Kata sandi minimal 6 karakter';
}
return null;
},
),
const SizedBox(height: AppConfig.spaceXXL),
SizedBox(
height: 56,
child: ElevatedButton(
onPressed: _isLoading ? null : _handleLogin,
child: _isLoading
? const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(color: Colors.white, strokeWidth: 3)
)
: const Text('Masuk'),
),
),
const SizedBox(height: AppConfig.spaceXL),
Text(
'Hubungi admin pusat untuk mendapatkan kredensial akun.',
style: AppConfig.bodySmall,
textAlign: TextAlign.center,
),
],
),
),
),
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../config/app_config.dart';
import '../../core/network/api_exception.dart';
import 'auth_provider.dart';
class LoginScreen extends ConsumerStatefulWidget {
const LoginScreen({super.key});
@override
ConsumerState<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends ConsumerState<LoginScreen> {
final _idController = TextEditingController();
final _passwordController = TextEditingController();
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
void _handleLogin() async {
if (!_formKey.currentState!.validate()) return;
final id = _idController.text.trim();
final pwd = _passwordController.text.trim();
setState(() => _isLoading = true);
try {
await ref.read(authProvider.notifier).login(id, pwd);
if (mounted) {
context.go('/camera');
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
ApiException.from(e).displayMessage,
style: const TextStyle(color: Colors.white),
),
backgroundColor: AppConfig.errorColor,
),
);
}
} finally {
if (mounted) {
setState(() => _isLoading = false);
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(
horizontal: AppConfig.spaceXXL,
vertical: AppConfig.spaceXL,
),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Image.asset(
'assets/logo.png',
width: 120,
height: 120,
errorBuilder: (context, error, stackTrace) => const Icon(
Icons.storefront,
size: 80,
color: AppConfig.primaryColor
),
),
const SizedBox(height: AppConfig.spaceXL),
Text(
'Selamat Datang Kembali',
style: AppConfig.h1,
textAlign: TextAlign.center,
),
const SizedBox(height: AppConfig.spaceSmall),
Text(
'Masuk untuk menyinkronkan dokumen Anda ke server pusat',
style: AppConfig.bodyMedium,
textAlign: TextAlign.center,
),
const SizedBox(height: AppConfig.spaceXXL * 1.5),
TextFormField(
controller: _idController,
decoration: const InputDecoration(
labelText: 'ID dari Admin',
prefixIcon: Icon(Icons.person_outline),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Username/ID tidak boleh kosong';
}
return null;
},
),
const SizedBox(height: AppConfig.spaceLarge),
TextFormField(
controller: _passwordController,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Kata Sandi',
prefixIcon: Icon(Icons.lock_outline),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Kata sandi tidak boleh kosong';
}
if (value.trim().length < 6) {
return 'Kata sandi minimal 6 karakter';
}
return null;
},
),
const SizedBox(height: AppConfig.spaceXXL),
SizedBox(
height: 56,
child: ElevatedButton(
onPressed: _isLoading ? null : _handleLogin,
child: _isLoading
? const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(color: Colors.white, strokeWidth: 3)
)
: const Text('Masuk'),
),
),
const SizedBox(height: AppConfig.spaceXL),
Text(
'Hubungi admin pusat untuk mendapatkan kredensial akun.',
style: AppConfig.bodySmall,
textAlign: TextAlign.center,
),
],
),
),
),
),
),
);
}
}