- Add date parameter support to DailyAgendaPage for calendar navigation - Add popup menu to task tile with delete and reschedule options - Set default locale to English when none is saved - Update API base URL comment for desktop vs Android
219 lines
6.3 KiB
Dart
219 lines
6.3 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:http/http.dart' as http;
|
|
|
|
import '../../../../core/errors/exceptions.dart';
|
|
import '../../../../core/logging/app_logger.dart';
|
|
import '../models/task_model.dart';
|
|
|
|
abstract class TaskRemoteDataSource {
|
|
Future<List<TaskModel>> getTasksByDate(String date, {String? status});
|
|
Future<TaskModel> getTaskById(String id);
|
|
Future<TaskModel> createTask(TaskModel task);
|
|
Future<TaskModel> updateTask(TaskModel task);
|
|
Future<void> deleteTask(String id);
|
|
Future<TaskModel> toggleTaskStatus(String id);
|
|
Future<TaskModel> rescheduleTask(String id, String targetDate);
|
|
}
|
|
|
|
class TaskRemoteDataSourceImpl implements TaskRemoteDataSource {
|
|
final AppLogger logger;
|
|
final http.Client _client;
|
|
|
|
// Use 10.0.2.2 for Android emulator, localhost for desktop/web
|
|
static const String _baseUrl = 'http://localhost:8000';
|
|
|
|
TaskRemoteDataSourceImpl({
|
|
required this.logger,
|
|
http.Client? client,
|
|
}) : _client = client ?? http.Client();
|
|
|
|
Map<String, String> get _headers => {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
};
|
|
|
|
@override
|
|
Future<List<TaskModel>> getTasksByDate(String date, {String? status}) async {
|
|
final queryParams = {'date': date};
|
|
if (status != null && status != 'all') {
|
|
queryParams['status'] = status;
|
|
}
|
|
|
|
final uri = Uri.parse('$_baseUrl/tasks').replace(queryParameters: queryParams);
|
|
logger.info('GET $uri');
|
|
|
|
try {
|
|
final response = await _client.get(uri, headers: _headers);
|
|
logger.debug('Response: ${response.statusCode}');
|
|
|
|
if (response.statusCode == 200) {
|
|
final List<dynamic> jsonList = json.decode(response.body);
|
|
return jsonList.map((json) => TaskModel.fromJson(json)).toList();
|
|
} else {
|
|
throw ServerException(
|
|
message: 'Failed to load tasks',
|
|
statusCode: response.statusCode,
|
|
);
|
|
}
|
|
} on SocketException {
|
|
throw const NetworkException();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<TaskModel> getTaskById(String id) async {
|
|
final uri = Uri.parse('$_baseUrl/tasks/$id');
|
|
logger.info('GET $uri');
|
|
|
|
try {
|
|
final response = await _client.get(uri, headers: _headers);
|
|
|
|
if (response.statusCode == 200) {
|
|
return TaskModel.fromJson(json.decode(response.body));
|
|
} else if (response.statusCode == 404) {
|
|
throw const NotFoundException(message: 'Task not found');
|
|
} else {
|
|
throw ServerException(
|
|
message: 'Failed to load task',
|
|
statusCode: response.statusCode,
|
|
);
|
|
}
|
|
} on SocketException {
|
|
throw const NetworkException();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<TaskModel> createTask(TaskModel task) async {
|
|
final uri = Uri.parse('$_baseUrl/tasks');
|
|
logger.info('POST $uri');
|
|
|
|
try {
|
|
final response = await _client.post(
|
|
uri,
|
|
headers: _headers,
|
|
body: json.encode(task.toCreateJson()),
|
|
);
|
|
logger.debug('Response: ${response.statusCode}');
|
|
|
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
|
return TaskModel.fromJson(json.decode(response.body));
|
|
} else if (response.statusCode == 400) {
|
|
throw ValidationException(
|
|
message: 'Invalid task data',
|
|
errors: json.decode(response.body),
|
|
);
|
|
} else {
|
|
throw ServerException(
|
|
message: 'Failed to create task',
|
|
statusCode: response.statusCode,
|
|
);
|
|
}
|
|
} on SocketException {
|
|
throw const NetworkException();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<TaskModel> updateTask(TaskModel task) async {
|
|
final uri = Uri.parse('$_baseUrl/tasks/${task.id}');
|
|
logger.info('PUT $uri');
|
|
|
|
try {
|
|
final response = await _client.put(
|
|
uri,
|
|
headers: _headers,
|
|
body: json.encode(task.toJson()),
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
return TaskModel.fromJson(json.decode(response.body));
|
|
} else if (response.statusCode == 404) {
|
|
throw const NotFoundException(message: 'Task not found');
|
|
} else {
|
|
throw ServerException(
|
|
message: 'Failed to update task',
|
|
statusCode: response.statusCode,
|
|
);
|
|
}
|
|
} on SocketException {
|
|
throw const NetworkException();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<void> deleteTask(String id) async {
|
|
final uri = Uri.parse('$_baseUrl/tasks/$id');
|
|
logger.info('DELETE $uri');
|
|
|
|
try {
|
|
final response = await _client.delete(uri, headers: _headers);
|
|
|
|
if (response.statusCode == 204 || response.statusCode == 200) {
|
|
return;
|
|
} else if (response.statusCode == 404) {
|
|
throw const NotFoundException(message: 'Task not found');
|
|
} else {
|
|
throw ServerException(
|
|
message: 'Failed to delete task',
|
|
statusCode: response.statusCode,
|
|
);
|
|
}
|
|
} on SocketException {
|
|
throw const NetworkException();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<TaskModel> toggleTaskStatus(String id) async {
|
|
final uri = Uri.parse('$_baseUrl/tasks/$id/toggle');
|
|
logger.info('PATCH $uri');
|
|
|
|
try {
|
|
final response = await _client.patch(uri, headers: _headers);
|
|
|
|
if (response.statusCode == 200) {
|
|
return TaskModel.fromJson(json.decode(response.body));
|
|
} else if (response.statusCode == 404) {
|
|
throw const NotFoundException(message: 'Task not found');
|
|
} else {
|
|
throw ServerException(
|
|
message: 'Failed to toggle task status',
|
|
statusCode: response.statusCode,
|
|
);
|
|
}
|
|
} on SocketException {
|
|
throw const NetworkException();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<TaskModel> rescheduleTask(String id, String targetDate) async {
|
|
final uri = Uri.parse('$_baseUrl/tasks/$id/reschedule');
|
|
logger.info('POST $uri');
|
|
|
|
try {
|
|
final response = await _client.post(
|
|
uri,
|
|
headers: _headers,
|
|
body: json.encode({'target_date': targetDate}),
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
return TaskModel.fromJson(json.decode(response.body));
|
|
} else if (response.statusCode == 404) {
|
|
throw const NotFoundException(message: 'Task not found');
|
|
} else {
|
|
throw ServerException(
|
|
message: 'Failed to reschedule task',
|
|
statusCode: response.statusCode,
|
|
);
|
|
}
|
|
} on SocketException {
|
|
throw const NetworkException();
|
|
}
|
|
}
|
|
}
|