feat: Initialize Flutter app with authentication and scheduling features
- Removed TypeScript configuration file (tsconfig.json). - Added Flutter plugin dependencies for secure storage. - Implemented main application structure with routing for login, profile, and schedule screens. - Developed LoginScreen with authentication logic and user feedback. - Created ProfileScreen to display user profile information and logout functionality. - Built ScheduleScreen to show weekly work schedule with navigation controls. - Integrated AuthService for handling authentication, token storage, and API interactions. - Updated pubspec.yaml with necessary dependencies for the Flutter project.
This commit is contained in:
83
lib/services/auth_service.dart
Normal file
83
lib/services/auth_service.dart
Normal file
@@ -0,0 +1,83 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
class AuthService extends ChangeNotifier {
|
||||
final _storage = const FlutterSecureStorage();
|
||||
String? token;
|
||||
Map<String, dynamic>? profile;
|
||||
final _base = 'https://menuapi.devbeni.lol/api';
|
||||
|
||||
AuthService() {
|
||||
_loadToken();
|
||||
}
|
||||
|
||||
/// Convenience wrapper that returns the Data array for schedule or empty list
|
||||
Future<List<dynamic>> fetchSchedule(int year, int month) async {
|
||||
final resp = await _httpGet('/@me/schedule?year=$year&month=$month');
|
||||
if (resp == null) return [];
|
||||
try {
|
||||
if (resp['data'] != null && resp['data']['Data'] is List) {
|
||||
return List<dynamic>.from(resp['data']['Data']);
|
||||
}
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
Future<void> loadProfile() async {
|
||||
if (token == null) return;
|
||||
final res = await http.get(
|
||||
Uri.parse('https://menuapi.devbeni.lol/api/me'),
|
||||
headers: {'Authorization': 'Bearer $token'},
|
||||
);
|
||||
if (res.statusCode == 200) {
|
||||
profile = jsonDecode(res.body)['data'];
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> login(String username, String password) async {
|
||||
try {
|
||||
final res = await http.post(
|
||||
Uri.parse('$_base/login'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonEncode({'username': username, 'password': password}),
|
||||
);
|
||||
if (res.statusCode != 200) return false;
|
||||
final data = jsonDecode(res.body);
|
||||
// token might be in different places; try common keys
|
||||
token = data['token'] ?? data['access_token'] ?? (data['data'] != null ? data['data']['token'] : null);
|
||||
if (token == null) return false;
|
||||
await _storage.write(key: 'token', value: token);
|
||||
await loadProfile();
|
||||
notifyListeners();
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> logout() async {
|
||||
token = null;
|
||||
profile = null;
|
||||
await _storage.delete(key: 'token');
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<dynamic> _httpGet(String path) async {
|
||||
if (token == null) return null;
|
||||
final res = await http.get(Uri.parse('$_base$path'), headers: {'Authorization': 'Bearer $token'});
|
||||
if (res.statusCode == 200) return jsonDecode(res.body);
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> _loadToken() async {
|
||||
token = await _storage.read(key: 'token');
|
||||
if (token != null) await loadProfile();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user