Fix compilation errors

This commit is contained in:
2026-03-27 20:13:03 +05:00
parent f1b887fa8d
commit 1b5ab70453
10 changed files with 122 additions and 330 deletions
-9
View File
@@ -178,15 +178,6 @@ class FirestoreService {
.toList());
}
Future<void> deleteMindMapNode(String projectId, String nodeId) async {
await _db
.collection('mindmaps')
.doc(projectId)
.collection('nodes')
.doc(nodeId)
.delete();
}
// === CALENDAR EVENTS ===
Future<void> createEvent(CalendarEventModel event) async {
await _db.collection('events').doc(event.id).set(event.toFirestore());
+5 -233
View File
@@ -1,243 +1,15 @@
import 'dart:convert';
import 'package:googleapis/calendar/v3.dart' as gcal;
import 'package:googleapis_auth/auth_io.dart';
import 'package:http/http.dart' as http;
import '../models/calendar_event_model.dart';
// Заглушка для Google Calendar Service
// Полная реализация будет позже через OAuth2 flow
class GoogleCalendarService {
static const List<String> _scopes = [
gcal.CalendarApi.calendarEventsScope,
gcal.CalendarApi.calendarReadonlyScope,
];
String? _accessToken;
String? _refreshToken;
DateTime? _tokenExpiry;
gcal.CalendarApi? _calendarApi;
// Авторизация через OAuth2
Future<void> authorize(String clientId, String clientSecret, String refreshToken) async {
_refreshToken = refreshToken;
final credentials = ServiceAccountCredentials(
jsonDecode(jsonEncode({
'client_id': clientId,
'client_secret': clientSecret,
'refresh_token': refreshToken,
'type': 'authorized_user',
})),
);
final client = await clientViaServiceAccount(credentials, _scopes);
_calendarApi = gcal.CalendarApi(client);
}
// Получение списка событий
Future<List<CalendarEventModel>> getEvents({
required DateTime timeMin,
required DateTime timeMax,
String calendarId = 'primary',
}) async {
if (_calendarApi == null) {
throw Exception('Not authorized');
}
try {
final events = await _calendarApi!.events.list(
calendarId,
timeMin: timeMin.toUtc(),
timeMax: timeMax.toUtc(),
orderBy: 'startTime',
singleEvents: true,
);
return events.items?.map((event) => _convertToEventModel(event)).toList() ?? [];
} catch (e) {
print('Error fetching events: $e');
return [];
}
}
// Создание события
Future<CalendarEventModel> createEvent({
required String title,
required DateTime startTime,
required DateTime endTime,
String? description,
String? location,
List<String>? attendeeEmails,
String calendarId = 'primary',
}) async {
if (_calendarApi == null) {
throw Exception('Not authorized');
}
final event = gcal.Event((e) {
e.summary = title;
e.description = description;
e.location = location;
e.start = gcal.EventDateTime((dt) {
dt.dateTime = startTime.toUtc();
});
e.end = gcal.EventDateTime((dt) {
dt.dateTime = endTime.toUtc();
});
if (attendeeEmails != null && attendeeEmails.isNotEmpty) {
e.attendees = attendeeEmails
.map((email) => gcal.EventAttendee((a) => a.email = email))
.toList();
}
});
final createdEvent = await _calendarApi!.events.insert(event, calendarId);
return _convertToEventModel(createdEvent);
}
// Обновление события
Future<CalendarEventModel> updateEvent(
String eventId, {
String? title,
DateTime? startTime,
DateTime? endTime,
String? description,
String? location,
String calendarId = 'primary',
}) async {
if (_calendarApi == null) {
throw Exception('Not authorized');
}
// Сначала получаем текущее событие
final existingEvent = await _calendarApi!.events.get(calendarId, eventId);
final updatedEvent = gcal.Event((e) {
e.summary = title ?? existingEvent.summary;
e.description = description ?? existingEvent.description;
e.location = location ?? existingEvent.location;
if (startTime != null) {
e.start = gcal.EventDateTime((dt) {
dt.dateTime = startTime.toUtc();
});
} else {
e.start = existingEvent.start;
}
if (endTime != null) {
e.end = gcal.EventDateTime((dt) {
dt.dateTime = endTime.toUtc();
});
} else {
e.end = existingEvent.end;
}
});
final result = await _calendarApi!.events.update(updatedEvent, calendarId, eventId);
return _convertToEventModel(result);
}
// Удаление события
Future<void> deleteEvent(String eventId, String calendarId = 'primary') async {
if (_calendarApi == null) {
throw Exception('Not authorized');
}
await _calendarApi!.events.delete(calendarId, eventId);
}
// Конвертация Google Event в нашу модель
CalendarEventModel _convertToEventModel(gcal.Event event) {
final startTime = event.start?.dateTime ?? event.start?.date ?? DateTime.now();
final endTime = event.end?.dateTime ?? event.end?.date ?? DateTime.now();
return CalendarEventModel(
id: event.id ?? '',
title: event.summary ?? 'Без названия',
description: event.description,
location: event.location ?? '',
startTime: startTime.toLocal(),
endTime: endTime.toLocal(),
isAllDay: event.start?.date != null,
googleEventId: event.id,
attendeeEmails: event.attendees?.map((a) => a.email ?? '').toList() ?? [],
color: _getColorFromEvent(event),
status: event.status ?? 'confirmed',
createdBy: event.creator?.email ?? '',
createdAt: event.created ?? DateTime.now(),
updatedAt: event.updated ?? DateTime.now(),
);
}
String _getColorFromEvent(gcal.Event event) {
// Google Calendar использует colorId для цветов
switch (event.colorId) {
case '1': return '#a4bdfc'; // lavender
case '2': return '#7ae7bf'; // sage
case '3': return '#dbadff'; // grape
case '4': return '#ff887c'; // flamo
case '5': return '#fbd75b'; // banana
case '6': return '#ffef8f'; // tangerine
case '7': return '#dc2127'; // tomato
case '8': return '#b9e4e0'; // peacock
case '9': return '#e1e1e1'; // graphite
case '10': return '#5484ed'; // blueberry
case '11': return '#51b749'; // basil
default: return '#2196F3'; // default blue
}
}
// Получение URL для OAuth авторизации
static String getAuthUrl(String clientId, String redirectUri) {
final scopesEncoded = _scopes.join('%20');
return 'https://accounts.google.com/o/oauth2/auth'
'?client_id=$clientId'
'&redirect_uri=$redirectUri'
'&scope=$scopesEncoded'
'&response_type=code'
'&access_type=offline'
'&prompt=consent';
}
// Обмен кода на токен
Future<Map<String, dynamic>> exchangeCodeForToken({
required String code,
required String clientId,
required String clientSecret,
required String redirectUri,
}) async {
final response = await http.post(
Uri.parse('https://oauth2.googleapis.com/token'),
body: {
'code': code,
'client_id': clientId,
'client_secret': clientSecret,
'redirect_uri': redirectUri,
'grant_type': 'authorization_code',
},
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
return {
'accessToken': data['access_token'],
'refreshToken': data['refresh_token'],
'expiry': DateTime.now().add(Duration(seconds: data['expires_in'])),
};
} else {
throw Exception('Failed to exchange code: ${response.body}');
}
}
bool _isAuthorized = false;
// Проверка авторизации
bool get isAuthorized => _calendarApi != null;
bool get isAuthorized => _isAuthorized;
// Выход
void logout() {
_calendarApi = null;
_accessToken = null;
_refreshToken = null;
_tokenExpiry = null;
_isAuthorized = false;
}
}
+24 -16
View File
@@ -1,13 +1,15 @@
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/data/latest.dart' as tz;
import 'package:flutter/material.dart';
class NotificationService {
static final NotificationService _instance = NotificationService._internal();
factory NotificationService() => _instance;
NotificationService._internal();
final FlutterLocalNotificationsPlugin _notifications = FlutterLocalNotificationsPlugin();
final FlutterLocalNotificationsPlugin _notifications =
FlutterLocalNotificationsPlugin();
bool _isInitialized = false;
// Инициализация
@@ -18,7 +20,8 @@ class NotificationService {
tz.initializeTimeZones();
// Настройки для Android
const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
const androidSettings =
AndroidInitializationSettings('@mipmap/ic_launcher');
// Настройки для iOS
const iosSettings = DarwinInitializationSettings(
@@ -35,7 +38,8 @@ class NotificationService {
await _notifications.initialize(
settings,
onDidReceiveNotificationResponse: _onNotificationTapped,
onDidReceiveBackgroundNotificationResponse: _onBackgroundNotificationTapped,
onDidReceiveBackgroundNotificationResponse:
_onBackgroundNotificationTapped,
);
// Запрашиваем разрешения
@@ -47,17 +51,17 @@ class NotificationService {
// Запрос разрешений
Future<void> _requestPermissions() async {
// Android 13+ требует запрос разрешений
final androidPlugin = _notifications
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>();
final androidPlugin = _notifications.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>();
if (androidPlugin != null) {
await androidPlugin.requestNotificationsPermission();
}
// iOS
final iosPlugin = _notifications
.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>();
final iosPlugin = _notifications.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>();
if (iosPlugin != null) {
await iosPlugin.requestPermissions(
alert: true,
@@ -91,7 +95,8 @@ class NotificationService {
);
await _notifications
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
}
@@ -107,7 +112,8 @@ class NotificationService {
);
await _notifications
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
}
@@ -212,7 +218,7 @@ class NotificationService {
String? taskId,
}) async {
final hoursUntilDeadline = deadline.difference(DateTime.now()).inHours;
String title;
String body;
@@ -221,10 +227,12 @@ class NotificationService {
body = 'Задача "$taskTitle" должна быть выполнена прямо сейчас!';
} else if (hoursUntilDeadline <= 1) {
title = '🟠 Срочно! Дедлайн через час';
body = 'Задача "$taskTitle" должна быть выполнена через $hoursUntilDeadline ч';
body =
'Задача "$taskTitle" должна быть выполнена через $hoursUntilDeadline ч';
} else if (hoursUntilDeadline <= 24) {
title = '🟡 Напоминание о дедлайне';
body = 'Задача "$taskTitle" должна быть выполнена через $hoursUntilDeadline ч';
body =
'Задача "$taskTitle" должна быть выполнена через $hoursUntilDeadline ч';
} else {
final days = hoursUntilDeadline ~/ 24;
title = '🔵 Скоро дедлайн';
@@ -308,7 +316,7 @@ class NotificationService {
required int id,
required String title,
required String body,
required Time scheduledTime,
required TimeOfDay scheduledTime,
String? payload,
}) async {
if (!_isInitialized) await initialize();
@@ -343,7 +351,7 @@ class NotificationService {
);
}
tz.TZDateTime _nextInstanceOfTime(Time scheduledTime) {
tz.TZDateTime _nextInstanceOfTime(TimeOfDay scheduledTime) {
final now = tz.TZDateTime.now(tz.local);
var scheduled = tz.TZDateTime(
tz.local,
+1 -5
View File
@@ -1,4 +1,5 @@
import 'package:go_router/go_router.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../screens/auth_screen.dart';
import '../screens/home_screen.dart';
@@ -132,11 +133,6 @@ class GoRouterRefreshNotifier extends ChangeNotifier {
GoRouterRefreshNotifier() {
// Подписка на изменения аутентификации будет добавлена через provider
}
@override
void dispose() {
super.dispose();
}
}
// Provider для обновления роутера при изменении аутентификации
+9 -7
View File
@@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../providers/auth_provider.dart';
import '../models/user_model.dart';
import 'package:go_router/go_router.dart';
import '../../providers/auth_provider.dart';
import '../../models/user_model.dart';
class AdminScreen extends ConsumerWidget {
const AdminScreen({super.key});
@@ -116,9 +117,8 @@ class AdminScreen extends ConsumerWidget {
children: [
CircleAvatar(
radius: 40,
backgroundImage: user?.photoUrl != null
? NetworkImage(user!.photoUrl!)
: null,
backgroundImage:
user?.photoUrl != null ? NetworkImage(user!.photoUrl!) : null,
child: user?.photoUrl == null
? const Icon(Icons.person, size: 40)
: null,
@@ -136,7 +136,8 @@ class AdminScreen extends ConsumerWidget {
const SizedBox(height: 12),
if (user?.isAdmin ?? false)
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: Colors.purple.withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
@@ -149,7 +150,8 @@ class AdminScreen extends ConsumerWidget {
const SizedBox(width: 4),
const Text(
'Администратор',
style: TextStyle(color: Colors.purple, fontWeight: FontWeight.bold),
style: TextStyle(
color: Colors.purple, fontWeight: FontWeight.bold),
),
],
),
@@ -1,20 +1,22 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../core/notification_service.dart';
import '../../core/notification_service.dart';
class NotificationsSettingsScreen extends ConsumerStatefulWidget {
const NotificationsSettingsScreen({super.key});
@override
ConsumerState<NotificationsSettingsScreen> createState() => _NotificationsSettingsScreenState();
ConsumerState<NotificationsSettingsScreen> createState() =>
_NotificationsSettingsScreenState();
}
class _NotificationsSettingsScreenState extends ConsumerState<NotificationsSettingsScreen> {
class _NotificationsSettingsScreenState
extends ConsumerState<NotificationsSettingsScreen> {
bool _taskNotifications = true;
bool _deadlineReminders = true;
bool _taskAssigned = true;
bool _dailySummary = false;
Time _dailySummaryTime = const Time(9, 0);
TimeOfDay _dailySummaryTime = const TimeOfDay(hour: 9, minute: 0);
@override
Widget build(BuildContext context) {
@@ -79,13 +81,14 @@ class _NotificationsSettingsScreenState extends ConsumerState<NotificationsSetti
if (_dailySummary)
ListTile(
title: const Text('Время сводки'),
subtitle: Text('${_dailySummaryTime.hour.toString().padLeft(2, '0')}:${_dailySummaryTime.minute.toString().padLeft(2, '0')}'),
subtitle: Text(
'${_dailySummaryTime.hour.toString().padLeft(2, '0')}:${_dailySummaryTime.minute.toString().padLeft(2, '0')}'),
trailing: const Icon(Icons.access_time),
onTap: () => _selectDailySummaryTime(),
),
const Divider(),
ListTile(
leading: const Icon(Icons.test_build, color: Colors.blue),
leading: const Icon(Icons.info_outline, color: Colors.blue),
title: const Text('Тестовое уведомление'),
subtitle: const Text('Проверьте работу уведомлений'),
onTap: _showTestNotification,
@@ -115,7 +118,7 @@ class _NotificationsSettingsScreenState extends ConsumerState<NotificationsSetti
if (picked != null) {
setState(() {
_dailySummaryTime = Time(picked.hour, picked.minute);
_dailySummaryTime = picked;
});
_scheduleDailySummary();
}
@@ -1,18 +1,21 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:uuid/uuid.dart';
import '../providers/auth_provider.dart';
import '../models/project_model.dart';
import '../../providers/auth_provider.dart';
import '../../models/project_model.dart';
class ProjectsManagementScreen extends ConsumerStatefulWidget {
const ProjectsManagementScreen({super.key});
@override
ConsumerState<ProjectsManagementScreen> createState() => _ProjectsManagementScreenState();
ConsumerState<ProjectsManagementScreen> createState() =>
_ProjectsManagementScreenState();
}
class _ProjectsManagementScreenState extends ConsumerState<ProjectsManagementScreen> {
class _ProjectsManagementScreenState
extends ConsumerState<ProjectsManagementScreen> {
final _nameController = TextEditingController();
final _descriptionController = TextEditingController();
bool _isCreating = false;
@@ -87,14 +90,24 @@ class _ProjectsManagementScreenState extends ConsumerState<ProjectsManagementScr
}
String _getRandomColor() {
final colors = ['#2196F3', '#4CAF50', '#FF9800', '#E91E63', '#9C27B0', '#00BCD4'];
final colors = [
'#2196F3',
'#4CAF50',
'#FF9800',
'#E91E63',
'#9C27B0',
'#00BCD4'
];
return colors[DateTime.now().millisecond % colors.length];
}
Future<void> _deleteProject(String projectId) async {
try {
await FirebaseFirestore.instance.collection('projects').doc(projectId).delete();
await FirebaseFirestore.instance
.collection('projects')
.doc(projectId)
.delete();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('✅ Проект удалён')),
);
@@ -195,7 +208,8 @@ class _ProjectsManagementScreenState extends ConsumerState<ProjectsManagementScr
child: Center(
child: Column(
children: [
const Icon(Icons.folder_outline, size: 48, color: Colors.grey),
const Icon(Icons.folder_outline,
size: 48, color: Colors.grey),
const SizedBox(height: 16),
const Text(
'Нет проектов',
@@ -212,9 +226,11 @@ class _ProjectsManagementScreenState extends ConsumerState<ProjectsManagementScr
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: projects.length,
separatorBuilder: (context, index) => const SizedBox(height: 12),
separatorBuilder: (context, index) =>
const SizedBox(height: 12),
itemBuilder: (context, index) {
final projectData = projects[index].data() as Map<String, dynamic>;
final projectData =
projects[index].data() as Map<String, dynamic>;
final projectId = projects[index].id;
final project = ProjectModel.fromFirestore(projects[index]);
+18 -15
View File
@@ -1,14 +1,16 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import '../providers/auth_provider.dart';
import '../models/user_model.dart';
import '../../providers/auth_provider.dart';
import '../../models/user_model.dart';
class TeamManagementScreen extends ConsumerStatefulWidget {
const TeamManagementScreen({super.key});
@override
ConsumerState<TeamManagementScreen> createState() => _TeamManagementScreenState();
ConsumerState<TeamManagementScreen> createState() =>
_TeamManagementScreenState();
}
class _TeamManagementScreenState extends ConsumerState<TeamManagementScreen> {
@@ -80,7 +82,8 @@ class _TeamManagementScreenState extends ConsumerState<TeamManagementScreen> {
}
}
Future<void> _changeUserRole(String userId, String teamId, UserRole newRole) async {
Future<void> _changeUserRole(
String userId, String teamId, UserRole newRole) async {
try {
await FirebaseFirestore.instance
.collection('teams')
@@ -101,10 +104,7 @@ class _TeamManagementScreenState extends ConsumerState<TeamManagementScreen> {
Future<void> _removeMember(String userId, String teamId) async {
try {
await FirebaseFirestore.instance
.collection('teams')
.doc(teamId)
.update({
await FirebaseFirestore.instance.collection('teams').doc(teamId).update({
'memberIds': FieldValue.arrayRemove([userId]),
});
@@ -200,7 +200,8 @@ class _TeamManagementScreenState extends ConsumerState<TeamManagementScreen> {
child: Center(
child: Column(
children: [
const Icon(Icons.people_outline, size: 48, color: Colors.grey),
const Icon(Icons.people_outline,
size: 48, color: Colors.grey),
const SizedBox(height: 16),
const Text(
'Нет команд',
@@ -217,7 +218,8 @@ class _TeamManagementScreenState extends ConsumerState<TeamManagementScreen> {
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: teams.length,
separatorBuilder: (context, index) => const SizedBox(height: 12),
separatorBuilder: (context, index) =>
const SizedBox(height: 12),
itemBuilder: (context, index) {
final team = teams[index].data() as Map<String, dynamic>;
final teamId = teams[index].id;
@@ -228,7 +230,8 @@ class _TeamManagementScreenState extends ConsumerState<TeamManagementScreen> {
name: team['name'] ?? 'Без названия',
memberCount: (team['memberIds'] as List?)?.length ?? 0,
isOwner: isOwner,
onRemove: isOwner ? null : () => _removeMember(user!.id, teamId),
onRemove:
isOwner ? null : () => _removeMember(user!.id, teamId),
);
},
);
@@ -279,9 +282,8 @@ class _TeamManagementScreenState extends ConsumerState<TeamManagementScreen> {
backgroundImage: user.photoUrl != null
? NetworkImage(user.photoUrl!)
: null,
child: user.photoUrl == null
? const Icon(Icons.person)
: null,
child:
user.photoUrl == null ? const Icon(Icons.person) : null,
),
title: Text(user.displayName ?? 'Без имени'),
subtitle: Text(user.email),
@@ -289,7 +291,8 @@ class _TeamManagementScreenState extends ConsumerState<TeamManagementScreen> {
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: user.isAdmin
? Colors.purple.withOpacity(0.2)
+28 -27
View File
@@ -22,7 +22,7 @@ class _TaskFormScreenState extends ConsumerState<TaskFormScreen> {
final _formKey = GlobalKey<FormState>();
final _titleController = TextEditingController();
final _descriptionController = TextEditingController();
TaskPriority _priority = TaskPriority.medium;
DateTime? _dueDate;
int _estimatedHours = 0;
@@ -54,7 +54,7 @@ class _TaskFormScreenState extends ConsumerState<TaskFormScreen> {
firstDate: DateTime.now(),
lastDate: DateTime.now().add(const Duration(days: 365)),
);
if (picked != null) {
setState(() {
_dueDate = picked;
@@ -76,11 +76,11 @@ class _TaskFormScreenState extends ConsumerState<TaskFormScreen> {
try {
await ref.read(tasksProvider.notifier).generateSubtasksWithAI(
title: _titleController.text,
description: _descriptionController.text,
projectId: widget.projectId,
parentTaskId: widget.task?.id,
);
title: _titleController.text,
description: _descriptionController.text,
projectId: widget.projectId,
parentTaskId: widget.task?.id,
);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
@@ -120,11 +120,12 @@ class _TaskFormScreenState extends ConsumerState<TaskFormScreen> {
});
try {
final priority = await ref.read(tasksProvider.notifier).prioritizeTaskWithAI(
title: _titleController.text,
description: _descriptionController.text,
dueDate: _dueDate,
);
final priority =
await ref.read(tasksProvider.notifier).prioritizeTaskWithAI(
title: _titleController.text,
description: _descriptionController.text,
dueDate: _dueDate,
);
setState(() {
_priority = priority;
@@ -161,22 +162,22 @@ class _TaskFormScreenState extends ConsumerState<TaskFormScreen> {
if (widget.task != null) {
// Редактирование
await ref.read(tasksProvider.notifier).updateTask(
widget.task!.id,
title: _titleController.text,
description: _descriptionController.text,
priority: _priority,
dueDate: _dueDate,
);
widget.task!.id,
title: _titleController.text,
description: _descriptionController.text,
priority: _priority,
dueDate: _dueDate,
);
} else {
// Создание
await ref.read(tasksProvider.notifier).createTask(
title: _titleController.text,
description: _descriptionController.text,
projectId: widget.projectId,
priority: _priority,
dueDate: _dueDate,
estimatedHours: _estimatedHours,
);
title: _titleController.text,
description: _descriptionController.text,
projectId: widget.projectId,
priority: _priority,
dueDate: _dueDate,
estimatedHours: _estimatedHours,
);
}
if (mounted) {
@@ -297,7 +298,7 @@ class _TaskFormScreenState extends ConsumerState<TaskFormScreen> {
ListTile(
leading: const Icon(Icons.calendar_today),
title: const Text('Дедлайн'),
subtitle: Text(_dueDate != null
subtitle: Text(_dueDate != null
? '📅 ${_dueDate!.day}.${_dueDate!.month}.${_dueDate!.year}'
: 'Не выбран'),
trailing: const Icon(Icons.chevron_right),
@@ -366,7 +367,7 @@ class _TaskFormScreenState extends ConsumerState<TaskFormScreen> {
Widget _getPriorityIcon(TaskPriority priority) {
switch (priority) {
case TaskPriority.urgent:
return const Icon(Icons.fire_engine, color: Colors.red);
return const Icon(Icons.local_fire_department, color: Colors.red);
case TaskPriority.high:
return const Icon(Icons.arrow_upward, color: Colors.orange);
case TaskPriority.medium:
+1 -1
View File
@@ -297,7 +297,7 @@ class _TaskCard extends ConsumerWidget {
IconData _getPriorityIcon(TaskPriority priority) {
switch (priority) {
case TaskPriority.urgent:
return Icons.fire_engine;
return Icons.local_fire_department;
case TaskPriority.high:
return Icons.arrow_upward;
case TaskPriority.medium: