1258 lines
54 KiB
C#
1258 lines
54 KiB
C#
using System.Diagnostics;
|
||
|
||
namespace CORP_MAUI.Controls;
|
||
|
||
public partial class DesktopLayout : ContentView
|
||
{
|
||
public DesktopLayout()
|
||
{
|
||
InitializeComponent();
|
||
Sidebar.NavigationRequested += OnNavigationRequested;
|
||
|
||
// По умолчанию показываем ленту новостей
|
||
LoadDefaultContent();
|
||
}
|
||
|
||
private void LoadDefaultContent()
|
||
{
|
||
// Создаём контент для ленты новостей в десктопном стиле
|
||
var newsContent = CreateDesktopPostsContent();
|
||
SetContent(newsContent);
|
||
}
|
||
|
||
private View CreateDesktopPostsContent()
|
||
{
|
||
// Заголовок страницы с поиском и кнопкой добавления
|
||
var header = new Grid
|
||
{
|
||
ColumnDefinitions = new ColumnDefinitionCollection
|
||
{
|
||
new ColumnDefinition(GridLength.Auto),
|
||
new ColumnDefinition(GridLength.Star),
|
||
new ColumnDefinition(GridLength.Auto)
|
||
},
|
||
Margin = new Thickness(0, 0, 0, 20)
|
||
};
|
||
|
||
var title = new Label
|
||
{
|
||
Text = "Лента новостей",
|
||
FontSize = 24,
|
||
FontAttributes = FontAttributes.Bold,
|
||
TextColor = Color.FromArgb("#333333"),
|
||
VerticalOptions = LayoutOptions.Center
|
||
};
|
||
|
||
// Поисковая строка (по центру)
|
||
var searchBox = new Border
|
||
{
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 20 },
|
||
BackgroundColor = Color.FromArgb("#F5F5F5"),
|
||
Stroke = Color.FromArgb("#E0E0E0"),
|
||
StrokeThickness = 1,
|
||
Padding = new Thickness(15, 8),
|
||
MaximumWidthRequest = 300,
|
||
HorizontalOptions = LayoutOptions.Center,
|
||
VerticalOptions = LayoutOptions.Center
|
||
};
|
||
var searchLayout = new HorizontalStackLayout { Spacing = 10 };
|
||
searchLayout.Children.Add(new Label { Text = "🔍", FontSize = 14, TextColor = Color.FromArgb("#888888"), VerticalOptions = LayoutOptions.Center });
|
||
searchLayout.Children.Add(new Entry { Placeholder = "Поиск по названию, автору или...", PlaceholderColor = Color.FromArgb("#888888"), BackgroundColor = Colors.Transparent, WidthRequest = 200 });
|
||
searchBox.Content = searchLayout;
|
||
|
||
// Кнопка добавления новости
|
||
var addButton = new HorizontalStackLayout
|
||
{
|
||
Spacing = 8,
|
||
VerticalOptions = LayoutOptions.Center
|
||
};
|
||
|
||
var plusBorder = new Border
|
||
{
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 12 },
|
||
BackgroundColor = Color.FromArgb("#0D6EFD"),
|
||
HeightRequest = 24,
|
||
WidthRequest = 24,
|
||
Content = new Label { Text = "+", FontSize = 16, TextColor = Colors.White, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center }
|
||
};
|
||
var addLabel = new Label
|
||
{
|
||
Text = "Новость",
|
||
FontSize = 16,
|
||
TextColor = Color.FromArgb("#0D6EFD"),
|
||
VerticalOptions = LayoutOptions.Center
|
||
};
|
||
addButton.Children.Add(plusBorder);
|
||
addButton.Children.Add(addLabel);
|
||
|
||
Grid.SetColumn(title, 0);
|
||
Grid.SetColumn(searchBox, 1);
|
||
Grid.SetColumn(addButton, 2);
|
||
header.Children.Add(title);
|
||
header.Children.Add(searchBox);
|
||
header.Children.Add(addButton);
|
||
|
||
// Контейнер для контента
|
||
// Вместо простого VerticalStackLayout используем Grid, чтобы выделить header и scrollable area
|
||
var root = new Grid
|
||
{
|
||
RowDefinitions = new RowDefinitionCollection
|
||
{
|
||
new RowDefinition { Height = GridLength.Auto },
|
||
new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }
|
||
},
|
||
VerticalOptions = LayoutOptions.Fill,
|
||
HorizontalOptions = LayoutOptions.Fill
|
||
};
|
||
root.Add(header);
|
||
Grid.SetRow(header, 0);
|
||
|
||
// Здесь будет ScrollView с постами (загружается асинхронно)
|
||
var postsContainer = new VerticalStackLayout
|
||
{
|
||
Spacing = 15,
|
||
HorizontalOptions = LayoutOptions.Fill
|
||
};
|
||
|
||
// Placeholder пока загружаются посты
|
||
var loadingIndicator = new ActivityIndicator
|
||
{
|
||
IsRunning = true,
|
||
Color = Color.FromArgb("#0D6EFD"),
|
||
HorizontalOptions = LayoutOptions.Center,
|
||
Margin = new Thickness(0, 50)
|
||
};
|
||
postsContainer.Children.Add(loadingIndicator);
|
||
|
||
// Корректно настраиваем ScrollView: Fill и layout-constraints
|
||
var scrollView = new ScrollView { Content = postsContainer, VerticalOptions = LayoutOptions.Fill, HorizontalOptions = LayoutOptions.Fill };
|
||
|
||
// Помещаем scrollView в фиксированную ячейку Grid
|
||
root.Add(scrollView);
|
||
Grid.SetRow(scrollView, 1);
|
||
|
||
// Загружаем посты асинхронно
|
||
_ = LoadPostsAsync(postsContainer);
|
||
|
||
return root;
|
||
}
|
||
|
||
private async Task LoadPostsAsync(VerticalStackLayout container)
|
||
{
|
||
try
|
||
{
|
||
var response = await ServiceProvider.ApiService.GetPostsPaginatedAsync();
|
||
var posts = response?.Results;
|
||
|
||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||
{
|
||
container.Children.Clear();
|
||
|
||
if (posts == null || !posts.Any())
|
||
{
|
||
container.Children.Add(new Label
|
||
{
|
||
Text = "Нет новостей",
|
||
TextColor = Color.FromArgb("#888888"),
|
||
HorizontalOptions = LayoutOptions.Center,
|
||
Margin = new Thickness(0, 50)
|
||
});
|
||
return;
|
||
}
|
||
|
||
foreach (var post in posts)
|
||
{
|
||
var postCard = CreatePostCard(post);
|
||
// Даем карточке разумные ограничения по ширине — чтобы не становилась слишком большой
|
||
if (postCard is View v)
|
||
{
|
||
v.HorizontalOptions = LayoutOptions.Fill;
|
||
v.WidthRequest = double.NaN; // позволяет занять доступную ширину
|
||
v.MaximumWidthRequest = 900; // не растягивать на весь экран
|
||
}
|
||
container.Children.Add(postCard);
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||
{
|
||
container.Children.Clear();
|
||
container.Children.Add(new Label
|
||
{
|
||
Text = $"Ошибка загрузки: {ex.Message}",
|
||
TextColor = Color.FromArgb("#FF0000"),
|
||
HorizontalOptions = LayoutOptions.Center,
|
||
Margin = new Thickness(0, 50)
|
||
});
|
||
});
|
||
}
|
||
}
|
||
|
||
private View CreatePostCard(CORP_MAUI.Models.Post post)
|
||
{
|
||
var card = new Border
|
||
{
|
||
BackgroundColor = Colors.White,
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 12 },
|
||
Stroke = Colors.Transparent,
|
||
Padding = new Thickness(20),
|
||
Shadow = new Shadow
|
||
{
|
||
Brush = new SolidColorBrush(Colors.Black),
|
||
Opacity = 0.1f,
|
||
Offset = new Point(0, 2),
|
||
Radius = 8
|
||
},
|
||
HorizontalOptions = LayoutOptions.Fill
|
||
};
|
||
|
||
var content = new VerticalStackLayout { Spacing = 12 };
|
||
|
||
// Автор и дата
|
||
var authorRow = new HorizontalStackLayout { Spacing = 10 };
|
||
|
||
var avatarImage = new Image
|
||
{
|
||
Source = post.Author?.DisplayAvatarSource,
|
||
Aspect = Aspect.AspectFill,
|
||
HeightRequest = 40,
|
||
WidthRequest = 40
|
||
};
|
||
|
||
var avatar = new Border
|
||
{
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 20 },
|
||
HeightRequest = 40,
|
||
WidthRequest = 40,
|
||
BackgroundColor = Color.FromArgb("#E0E0E0"),
|
||
Stroke = Colors.Transparent,
|
||
Content = avatarImage
|
||
};
|
||
|
||
var authorInfo = new VerticalStackLayout();
|
||
authorInfo.Children.Add(new Label
|
||
{
|
||
Text = post.Author?.FullName ?? "Автор",
|
||
FontAttributes = FontAttributes.Bold,
|
||
TextColor = Color.FromArgb("#333333")
|
||
});
|
||
authorInfo.Children.Add(new Label
|
||
{
|
||
Text = post.CreatedAt.ToString("dd.MM.yyyy HH:mm"),
|
||
FontSize = 12,
|
||
TextColor = Color.FromArgb("#888888")
|
||
});
|
||
authorRow.Children.Add(avatar);
|
||
authorRow.Children.Add(authorInfo);
|
||
content.Children.Add(authorRow);
|
||
|
||
// Изображение поста (если есть)
|
||
if (!string.IsNullOrEmpty(post.DisplayImageUrl))
|
||
{
|
||
var imageContainer = new Border
|
||
{
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 8 },
|
||
Stroke = Colors.Transparent,
|
||
HeightRequest = 300,
|
||
Margin = new Thickness(0, 10)
|
||
};
|
||
var postImage = new Image
|
||
{
|
||
Source = post.DisplayImageUrl,
|
||
Aspect = Aspect.AspectFill,
|
||
HeightRequest = 300,
|
||
HorizontalOptions = LayoutOptions.Fill
|
||
};
|
||
imageContainer.Content = postImage;
|
||
content.Children.Add(imageContainer);
|
||
}
|
||
|
||
// Заголовок
|
||
if (!string.IsNullOrEmpty(post.Title))
|
||
{
|
||
content.Children.Add(new Label
|
||
{
|
||
Text = post.Title,
|
||
FontSize = 18,
|
||
FontAttributes = FontAttributes.Bold,
|
||
TextColor = Color.FromArgb("#0D6EFD")
|
||
});
|
||
}
|
||
|
||
// Текст
|
||
if (!string.IsNullOrEmpty(post.Content))
|
||
{
|
||
var isTruncated = post.Content.Length > 200;
|
||
var textContent = isTruncated
|
||
? post.Content.Substring(0, 200) + "..."
|
||
: post.Content;
|
||
|
||
var textRow = new HorizontalStackLayout { Spacing = 5 };
|
||
textRow.Children.Add(new Label
|
||
{
|
||
Text = textContent,
|
||
TextColor = Color.FromArgb("#333333"),
|
||
LineBreakMode = LineBreakMode.WordWrap,
|
||
HorizontalOptions = LayoutOptions.FillAndExpand
|
||
});
|
||
|
||
if (isTruncated)
|
||
{
|
||
textRow.Children.Add(new Label
|
||
{
|
||
Text = "Показать дальше",
|
||
TextColor = Color.FromArgb("#0D6EFD"),
|
||
FontSize = 14,
|
||
VerticalOptions = LayoutOptions.End
|
||
});
|
||
}
|
||
|
||
content.Children.Add(textRow);
|
||
}
|
||
|
||
// Лайки и комментарии
|
||
var statsRow = new HorizontalStackLayout { Spacing = 20, Margin = new Thickness(0, 10, 0, 0) };
|
||
statsRow.Children.Add(new HorizontalStackLayout
|
||
{
|
||
Spacing = 5,
|
||
Children =
|
||
{
|
||
new Label { Text = "👍", FontSize = 14 },
|
||
new Label { Text = post.LikesCount.ToString(), TextColor = Color.FromArgb("#888888") }
|
||
}
|
||
});
|
||
statsRow.Children.Add(new HorizontalStackLayout
|
||
{
|
||
Spacing = 5,
|
||
Children =
|
||
{
|
||
new Label { Text = "💬", FontSize = 14 },
|
||
new Label { Text = post.CommentsCount.ToString(), TextColor = Color.FromArgb("#888888") }
|
||
}
|
||
});
|
||
content.Children.Add(statsRow);
|
||
|
||
card.Content = content;
|
||
return card;
|
||
}
|
||
|
||
public void SetContent(View content)
|
||
{
|
||
MainContent.Content = content;
|
||
}
|
||
|
||
private void OnNavigationRequested(object? sender, string route)
|
||
{
|
||
switch (route)
|
||
{
|
||
case "Profile":
|
||
LoadProfileContent();
|
||
break;
|
||
case "News":
|
||
LoadDefaultContent();
|
||
break;
|
||
case "Employees":
|
||
LoadEmployeesContent();
|
||
break;
|
||
case "Departments":
|
||
LoadDepartmentsContent();
|
||
break;
|
||
case "Requests":
|
||
LoadRequestsContent();
|
||
break;
|
||
case "Chats":
|
||
LoadChatsContent();
|
||
break;
|
||
case "Documents":
|
||
LoadDocumentsContent();
|
||
break;
|
||
}
|
||
}
|
||
|
||
private void LoadProfileContent()
|
||
{
|
||
var content = new VerticalStackLayout { Spacing = 15 };
|
||
content.Children.Add(CreatePageHeader("Мой профиль"));
|
||
|
||
var loadingIndicator = new ActivityIndicator
|
||
{
|
||
IsRunning = true,
|
||
Color = Color.FromArgb("#0D6EFD"),
|
||
HorizontalOptions = LayoutOptions.Center,
|
||
Margin = new Thickness(0, 50)
|
||
};
|
||
content.Children.Add(loadingIndicator);
|
||
|
||
SetContent(content);
|
||
_ = LoadProfileAsync(content);
|
||
}
|
||
|
||
private async Task LoadProfileAsync(VerticalStackLayout container)
|
||
{
|
||
try
|
||
{
|
||
var user = await ServiceProvider.ApiService.GetCurrentUserAsync();
|
||
|
||
// Предзагружаем аватар
|
||
if (user != null)
|
||
{
|
||
await ServiceProvider.ApiService.LoadUserAvatarAsync(user);
|
||
}
|
||
|
||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||
{
|
||
container.Children.Clear();
|
||
container.Children.Add(CreatePageHeader("Мой профиль"));
|
||
|
||
if (user == null)
|
||
{
|
||
container.Children.Add(new Label { Text = "Не удалось загрузить профиль", TextColor = Colors.Red });
|
||
return;
|
||
}
|
||
|
||
var card = new Border
|
||
{
|
||
BackgroundColor = Colors.White,
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 12 },
|
||
Stroke = Colors.Transparent,
|
||
Padding = new Thickness(30),
|
||
Shadow = new Shadow { Brush = new SolidColorBrush(Colors.Black), Opacity = 0.1f, Offset = new Point(0, 2), Radius = 8 }
|
||
};
|
||
|
||
var profileContent = new HorizontalStackLayout { Spacing = 30 };
|
||
|
||
// Аватар
|
||
var avatarImage = new Image
|
||
{
|
||
Source = user.DisplayAvatarSource,
|
||
Aspect = Aspect.AspectFill,
|
||
HeightRequest = 120,
|
||
WidthRequest = 120
|
||
};
|
||
var avatarBorder = new Border
|
||
{
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 60 },
|
||
HeightRequest = 120,
|
||
WidthRequest = 120,
|
||
BackgroundColor = Color.FromArgb("#E0E0E0"),
|
||
Stroke = Colors.Transparent,
|
||
Content = avatarImage
|
||
};
|
||
|
||
// Индикатор онлайн
|
||
if (user.IsOnline)
|
||
{
|
||
var onlineIndicator = new Border
|
||
{
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 8 },
|
||
HeightRequest = 16,
|
||
WidthRequest = 16,
|
||
BackgroundColor = Color.FromArgb("#28A745"),
|
||
Stroke = Colors.White,
|
||
StrokeThickness = 2,
|
||
VerticalOptions = LayoutOptions.End,
|
||
HorizontalOptions = LayoutOptions.End,
|
||
Margin = new Thickness(0, 0, 8, 8)
|
||
};
|
||
var avatarGrid = new Grid { Children = { avatarBorder, onlineIndicator } };
|
||
profileContent.Children.Add(avatarGrid);
|
||
}
|
||
else
|
||
{
|
||
profileContent.Children.Add(avatarBorder);
|
||
}
|
||
|
||
// Информация о пользователе
|
||
var infoStack = new VerticalStackLayout { Spacing = 10 };
|
||
infoStack.Children.Add(new Label { Text = user.FullName, FontSize = 24, FontAttributes = FontAttributes.Bold, TextColor = Color.FromArgb("#333333") });
|
||
|
||
if (!string.IsNullOrEmpty(user.PositionName))
|
||
infoStack.Children.Add(new Label { Text = user.PositionName, FontSize = 16, TextColor = Color.FromArgb("#0D6EFD") });
|
||
|
||
infoStack.Children.Add(new Label { Text = user.Email, FontSize = 14, TextColor = Color.FromArgb("#666666") });
|
||
|
||
if (!string.IsNullOrEmpty(user.PhoneNumber))
|
||
infoStack.Children.Add(new Label { Text = user.PhoneNumber, FontSize = 14, TextColor = Color.FromArgb("#666666") });
|
||
|
||
// Отделы
|
||
if (user.Departments != null && user.Departments.Any())
|
||
{
|
||
infoStack.Children.Add(new Label { Text = "Отделы:", FontSize = 13, TextColor = Color.FromArgb("#888888"), Margin = new Thickness(0, 10, 0, 0) });
|
||
foreach (var dept in user.Departments)
|
||
{
|
||
infoStack.Children.Add(new Label { Text = $"• {dept.Name}", FontSize = 14, TextColor = Color.FromArgb("#404040") });
|
||
}
|
||
}
|
||
|
||
// Навыки
|
||
if (user.Skills != null && user.Skills.Any())
|
||
{
|
||
infoStack.Children.Add(new Label { Text = "Навыки:", FontSize = 13, TextColor = Color.FromArgb("#888888"), Margin = new Thickness(0, 10, 0, 0) });
|
||
var skillsLayout = new FlexLayout { Wrap = Microsoft.Maui.Layouts.FlexWrap.Wrap, JustifyContent = Microsoft.Maui.Layouts.FlexJustify.Start };
|
||
foreach (var skill in user.Skills)
|
||
{
|
||
var skillBadge = new Border
|
||
{
|
||
Padding = new Thickness(10, 5),
|
||
Margin = new Thickness(0, 5, 8, 0),
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 12 },
|
||
BackgroundColor = Color.FromArgb("#E8F4FD"),
|
||
Content = new Label { Text = skill.Name, FontSize = 12, TextColor = Color.FromArgb("#0D6EFD") }
|
||
};
|
||
skillsLayout.Children.Add(skillBadge);
|
||
}
|
||
infoStack.Children.Add(skillsLayout);
|
||
}
|
||
|
||
profileContent.Children.Add(infoStack);
|
||
card.Content = profileContent;
|
||
container.Children.Add(card);
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||
{
|
||
container.Children.Clear();
|
||
container.Children.Add(new Label { Text = $"Ошибка: {ex.Message}", TextColor = Colors.Red });
|
||
});
|
||
}
|
||
}
|
||
|
||
private void LoadEmployeesContent()
|
||
{
|
||
var content = new VerticalStackLayout { Spacing = 15 };
|
||
content.Children.Add(CreatePageHeader("Сотрудники"));
|
||
|
||
var container = new VerticalStackLayout { Spacing = 10 };
|
||
container.Children.Add(new ActivityIndicator { IsRunning = true, Color = Color.FromArgb("#0D6EFD"), HorizontalOptions = LayoutOptions.Center, Margin = new Thickness(0, 50) });
|
||
// Устанавливаем FillAndExpand чтобы контейнер занял пространство внутри MainContent
|
||
content.Children.Add(new ScrollView { Content = container, VerticalOptions = LayoutOptions.Fill, HorizontalOptions = LayoutOptions.Fill });
|
||
|
||
SetContent(content);
|
||
_ = LoadEmployeesAsync(container);
|
||
}
|
||
|
||
private async Task LoadEmployeesAsync(VerticalStackLayout container)
|
||
{
|
||
try
|
||
{
|
||
var response = await ServiceProvider.ApiService.GetEmployeesPaginatedAsync();
|
||
var employees = response?.Results;
|
||
|
||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||
{
|
||
container.Children.Clear();
|
||
|
||
if (employees == null || !employees.Any())
|
||
{
|
||
container.Children.Add(new Label { Text = "Нет сотрудников", TextColor = Color.FromArgb("#888888"), HorizontalOptions = LayoutOptions.Center });
|
||
return;
|
||
}
|
||
|
||
foreach (var emp in employees)
|
||
{
|
||
var card = new Border
|
||
{
|
||
BackgroundColor = Colors.White,
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 8 },
|
||
Stroke = Colors.Transparent,
|
||
Padding = new Thickness(15),
|
||
Margin = new Thickness(0, 0, 0, 5),
|
||
HorizontalOptions = LayoutOptions.Fill
|
||
};
|
||
|
||
var row = new HorizontalStackLayout { Spacing = 15 };
|
||
|
||
// Аватарка сотрудника
|
||
var avatarImage = new Image
|
||
{
|
||
Source = emp.DisplayAvatarSource,
|
||
Aspect = Aspect.AspectFill,
|
||
HeightRequest = 40,
|
||
WidthRequest = 40
|
||
};
|
||
|
||
var avatarBorder = new Border
|
||
{
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 20 },
|
||
HeightRequest = 40,
|
||
WidthRequest = 40,
|
||
BackgroundColor = Color.FromArgb("#E0E0E0"),
|
||
Stroke = Colors.Transparent,
|
||
Content = avatarImage
|
||
};
|
||
|
||
row.Children.Add(avatarBorder);
|
||
|
||
var info = new VerticalStackLayout();
|
||
info.Children.Add(new Label { Text = emp.FullName, FontAttributes = FontAttributes.Bold, TextColor = Color.FromArgb("#333333") });
|
||
info.Children.Add(new Label { Text = emp.PositionName ?? emp.Email, FontSize = 12, TextColor = Color.FromArgb("#888888") });
|
||
row.Children.Add(info);
|
||
|
||
card.Content = row;
|
||
|
||
// Добавляем обработчик нажатия для открытия профиля сотрудника
|
||
var tapGesture = new TapGestureRecognizer();
|
||
int employeeId = emp.Id; // Захватываем ID для замыкания
|
||
tapGesture.Tapped += async (_, _) =>
|
||
{
|
||
try
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"[DesktopLayout] >>> Открытие профиля сотрудника ID: {employeeId}");
|
||
var nav = ResolveNavigation();
|
||
if (nav != null)
|
||
{
|
||
await nav.PushAsync(new CORP_MAUI.Pages.EmployeeProfilePage(employeeId));
|
||
return;
|
||
}
|
||
|
||
System.Diagnostics.Debug.WriteLine($"[DesktopLayout] ❌ Не удалось найти навигацию для открытия профиля {employeeId}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"[DesktopLayout] ❌ Ошибка навигации к профилю: {ex.Message}");
|
||
}
|
||
};
|
||
card.GestureRecognizers.Add(tapGesture);
|
||
|
||
container.Children.Add(card);
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||
{
|
||
container.Children.Clear();
|
||
container.Children.Add(new Label { Text = $"Ошибка: {ex.Message}", TextColor = Colors.Red });
|
||
});
|
||
}
|
||
}
|
||
|
||
private void LoadDepartmentsContent()
|
||
{
|
||
// Заголовок с количеством и поиском
|
||
var headerRow = new Grid
|
||
{
|
||
ColumnDefinitions = new ColumnDefinitionCollection
|
||
{
|
||
new ColumnDefinition(GridLength.Auto),
|
||
new ColumnDefinition(GridLength.Star),
|
||
new ColumnDefinition(GridLength.Auto)
|
||
},
|
||
Margin = new Thickness(0, 0, 0, 20)
|
||
};
|
||
|
||
var titleStack = new VerticalStackLayout();
|
||
titleStack.Children.Add(new Label { Text = "Отделы", FontSize = 24, FontAttributes = FontAttributes.Bold, TextColor = Color.FromArgb("#333333") });
|
||
var countLabel = new Label { Text = "Всего отделов: ...", FontSize = 13, TextColor = Color.FromArgb("#888888") };
|
||
titleStack.Children.Add(countLabel);
|
||
Grid.SetColumn(titleStack, 0);
|
||
headerRow.Children.Add(titleStack);
|
||
|
||
// Поисковая строка
|
||
var searchBox = new Border
|
||
{
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 20 },
|
||
BackgroundColor = Color.FromArgb("#F5F5F5"),
|
||
Stroke = Color.FromArgb("#E0E0E0"),
|
||
StrokeThickness = 1,
|
||
Padding = new Thickness(15, 8),
|
||
MaximumWidthRequest = 300,
|
||
HorizontalOptions = LayoutOptions.End,
|
||
VerticalOptions = LayoutOptions.Center
|
||
};
|
||
var searchLayout = new HorizontalStackLayout { Spacing = 10 };
|
||
searchLayout.Children.Add(new Image { Source = "search", HeightRequest = 16, WidthRequest = 16 });
|
||
searchLayout.Children.Add(new Entry { Placeholder = "Поиск по названию, автору", PlaceholderColor = Color.FromArgb("#888888"), BackgroundColor = Colors.Transparent, WidthRequest = 200 });
|
||
searchBox.Content = searchLayout;
|
||
Grid.SetColumn(searchBox, 2);
|
||
headerRow.Children.Add(searchBox);
|
||
|
||
// Контейнер для сетки отделов с прокруткой
|
||
var gridContainer = new VerticalStackLayout { Spacing = 12, VerticalOptions = LayoutOptions.Fill, HorizontalOptions = LayoutOptions.Fill };
|
||
|
||
// Добавляем ActivityIndicator пока загружается
|
||
var activityIndicator = new ActivityIndicator
|
||
{
|
||
IsRunning = true,
|
||
Color = Color.FromArgb("#0D6EFD"),
|
||
HorizontalOptions = LayoutOptions.Center,
|
||
VerticalOptions = LayoutOptions.Center,
|
||
Margin = new Thickness(0, 50)
|
||
};
|
||
gridContainer.Children.Add(activityIndicator);
|
||
|
||
// Используем ScrollView вокруг контейнера (и обеспечиваем FillAndExpand у scrollView)
|
||
var scrollView = new ScrollView
|
||
{
|
||
Content = gridContainer,
|
||
VerticalOptions = LayoutOptions.Fill,
|
||
HorizontalOptions = LayoutOptions.Fill
|
||
};
|
||
|
||
// Основной контейнер
|
||
var content = new Grid
|
||
{
|
||
RowDefinitions = new RowDefinitionCollection
|
||
{
|
||
new RowDefinition { Height = GridLength.Auto },
|
||
new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }
|
||
},
|
||
VerticalOptions = LayoutOptions.Fill,
|
||
HorizontalOptions = LayoutOptions.Fill
|
||
};
|
||
|
||
Grid.SetRow(headerRow, 0);
|
||
content.Children.Add(headerRow);
|
||
|
||
Grid.SetRow(scrollView, 1);
|
||
content.Children.Add(scrollView);
|
||
|
||
SetContent(content);
|
||
_ = LoadDepartmentsAsync(gridContainer, countLabel);
|
||
}
|
||
|
||
private async Task LoadDepartmentsAsync(VerticalStackLayout container, Label countLabel)
|
||
{
|
||
try
|
||
{
|
||
System.Diagnostics.Debug.WriteLine("[DesktopLayout] >>> Начало загрузки отделов");
|
||
var departments = await ServiceProvider.ApiService.GetAllDepartmentsAsync();
|
||
System.Diagnostics.Debug.WriteLine($"[DesktopLayout] Получено отделов: {departments?.Count ?? 0}");
|
||
|
||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||
{
|
||
System.Diagnostics.Debug.WriteLine("[DesktopLayout] Очистка контейнера...");
|
||
container.Children.Clear();
|
||
|
||
if (departments == null || !departments.Any())
|
||
{
|
||
System.Diagnostics.Debug.WriteLine("[DesktopLayout] ⚠️ Нет отделов для отображения");
|
||
var emptyLabel = new Label { Text = "Нет отделов", TextColor = Color.FromArgb("#888888"), HorizontalOptions = LayoutOptions.Center };
|
||
container.Children.Add(emptyLabel);
|
||
return;
|
||
}
|
||
|
||
countLabel.Text = $"Всего отделов: {departments.Count}";
|
||
System.Diagnostics.Debug.WriteLine($"[DesktopLayout] Создание {departments.Count} карточек отделов...");
|
||
|
||
// Для корректной прокрутки и размеров используем Grid с равномерными колонками (Star) и поместим его в ScrollView
|
||
var grid = new Grid
|
||
{
|
||
ColumnDefinitions = new ColumnDefinitionCollection
|
||
{
|
||
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
|
||
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }
|
||
},
|
||
ColumnSpacing = 16,
|
||
RowSpacing = 16,
|
||
HorizontalOptions = LayoutOptions.Fill
|
||
};
|
||
|
||
// Добавляем карточки в Grid
|
||
for (int i = 0; i < departments.Count; i++)
|
||
{
|
||
var card = CreateDepartmentCard(departments[i]);
|
||
int row = i / 2;
|
||
int col = i % 2;
|
||
|
||
// Добавляем строку если нужно
|
||
if (row >= grid.RowDefinitions.Count)
|
||
{
|
||
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
|
||
}
|
||
|
||
Grid.SetRow(card, row);
|
||
Grid.SetColumn(card, col);
|
||
grid.Children.Add(card);
|
||
|
||
System.Diagnostics.Debug.WriteLine($"[DesktopLayout] ✅ Добавлена карточка {i + 1} в позицию [{row}, {col}]");
|
||
}
|
||
|
||
// Оборачиваем grid в контейнер, даём ему FillAndExpand чтобы прокрутка работала корректно
|
||
grid.HorizontalOptions = LayoutOptions.Fill;
|
||
container.Children.Add(grid);
|
||
System.Diagnostics.Debug.WriteLine($"[DesktopLayout] ✅ Grid создан с {grid.RowDefinitions.Count} строками");
|
||
|
||
// Загружаем аватарки сотрудников для каждого отдела
|
||
foreach (var dept in departments)
|
||
{
|
||
_ = LoadDepartmentEmployeesAsync(dept);
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"[DesktopLayout] ❌ Ошибка загрузки отделов: {ex.Message}");
|
||
System.Diagnostics.Debug.WriteLine($"[DesktopLayout] Stack trace: {ex.StackTrace}");
|
||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||
{
|
||
container.Children.Clear();
|
||
var errorLabel = new Label { Text = $"Ошибка: {ex.Message}", TextColor = Colors.Red };
|
||
container.Children.Add(errorLabel);
|
||
});
|
||
}
|
||
}
|
||
|
||
private View CreateDepartmentCard(CORP_MAUI.Models.Department dept)
|
||
{
|
||
var card = new Border
|
||
{
|
||
BackgroundColor = Colors.White,
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 12 },
|
||
Stroke = Color.FromArgb("#E0E0E0"),
|
||
StrokeThickness = 1,
|
||
Padding = new Thickness(16),
|
||
Margin = new Thickness(0),
|
||
HorizontalOptions = LayoutOptions.Fill
|
||
};
|
||
|
||
var content = new VerticalStackLayout { Spacing = 8 };
|
||
|
||
// Название отдела
|
||
content.Children.Add(new Label
|
||
{
|
||
Text = dept.Name,
|
||
FontSize = 15,
|
||
FontAttributes = FontAttributes.Bold,
|
||
TextColor = Color.FromArgb("#333333"),
|
||
LineBreakMode = LineBreakMode.TailTruncation,
|
||
MaxLines = 1
|
||
});
|
||
|
||
// Руководитель
|
||
content.Children.Add(new Label
|
||
{
|
||
Text = "Руководитель:",
|
||
FontSize = 11,
|
||
TextColor = Color.FromArgb("#888888")
|
||
});
|
||
|
||
string headName = dept.Head?.FullName ?? "Не назначен";
|
||
content.Children.Add(new Label
|
||
{
|
||
Text = headName,
|
||
FontSize = 12,
|
||
TextColor = Color.FromArgb("#0D6EFD"),
|
||
FontAttributes = FontAttributes.None,
|
||
LineBreakMode = LineBreakMode.TailTruncation,
|
||
MaxLines = 1
|
||
});
|
||
|
||
// Количество сотрудников
|
||
content.Children.Add(new Label
|
||
{
|
||
Text = $"{dept.EmployeesCount} сотрудников",
|
||
FontSize = 11,
|
||
TextColor = Color.FromArgb("#666666"),
|
||
Margin = new Thickness(0, 4, 0, 4)
|
||
});
|
||
|
||
// Аватарки сотрудников (серые кружки, которые потом заменятся на реальные)
|
||
var avatarsRow = new HorizontalStackLayout { Spacing = -8, HorizontalOptions = LayoutOptions.Start };
|
||
|
||
// Сохраняем ссылку на контейнер аватарок в свойстве отдела для последующего обновления
|
||
dept.AvatarsContainer = avatarsRow;
|
||
|
||
int avatarCount = Math.Min(dept.EmployeesCount, 5);
|
||
for (int i = 0; i < avatarCount; i++)
|
||
{
|
||
var avatar = new Border
|
||
{
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 15 },
|
||
HeightRequest = 30,
|
||
WidthRequest = 30,
|
||
BackgroundColor = Color.FromArgb("#E0E0E0"),
|
||
Stroke = Colors.White,
|
||
StrokeThickness = 2
|
||
};
|
||
avatarsRow.Children.Add(avatar);
|
||
}
|
||
content.Children.Add(avatarsRow);
|
||
|
||
card.Content = content;
|
||
|
||
// Добавляем обработчик нажатия на карточку для навигации
|
||
var tapGesture = new TapGestureRecognizer();
|
||
int deptId = dept.Id; // Захватываем ID для замыкания
|
||
tapGesture.Tapped += async (_, _) =>
|
||
{
|
||
try
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"[DesktopLayout] >>> Открытие отдела ID: {deptId}");
|
||
var nav = ResolveNavigation();
|
||
if (nav != null)
|
||
{
|
||
await nav.PushAsync(new CORP_MAUI.Pages.DepartmentDetailsPage(deptId));
|
||
return;
|
||
}
|
||
|
||
System.Diagnostics.Debug.WriteLine($"[DesktopLayout] ❌ Не удалось найти навигацию для открытия отдела {deptId}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"[DesktopLayout] ❌ Ошибка навигации к отделу: {ex.Message}");
|
||
}
|
||
};
|
||
card.GestureRecognizers.Add(tapGesture);
|
||
|
||
return card;
|
||
}
|
||
|
||
private async Task LoadDepartmentEmployeesAsync(CORP_MAUI.Models.Department dept)
|
||
{
|
||
try
|
||
{
|
||
// Получаем сотрудников отдела
|
||
var employees = await ServiceProvider.ApiService.GetDepartmentEmployeesAsync(dept.Id);
|
||
|
||
if (employees != null && employees.Any())
|
||
{
|
||
// Обновляем аватарки в UI
|
||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||
{
|
||
// Найдем карточку отдела и обновим аватарки
|
||
if (dept.AvatarsContainer != null)
|
||
{
|
||
dept.AvatarsContainer.Children.Clear();
|
||
|
||
int avatarCount = Math.Min(employees.Count, 5);
|
||
for (int i = 0; i < avatarCount; i++)
|
||
{
|
||
var emp = employees[i];
|
||
var avatarImage = new Image
|
||
{
|
||
Source = emp.DisplayAvatarSource,
|
||
Aspect = Aspect.AspectFill,
|
||
HeightRequest = 30,
|
||
WidthRequest = 30
|
||
};
|
||
|
||
var avatar = new Border
|
||
{
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 15 },
|
||
HeightRequest = 30,
|
||
WidthRequest = 30,
|
||
BackgroundColor = Color.FromArgb("#E0E0E0"),
|
||
Stroke = Colors.White,
|
||
StrokeThickness = 2,
|
||
Content = avatarImage
|
||
};
|
||
dept.AvatarsContainer.Children.Add(avatar);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"[DesktopLayout] Ошибка загрузки сотрудников отдела {dept.Id}: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void LoadRequestsContent()
|
||
{
|
||
var content = new VerticalStackLayout { Spacing = 15 };
|
||
content.Children.Add(CreatePageHeader("Заявления"));
|
||
|
||
var container = new VerticalStackLayout { Spacing = 10 };
|
||
container.Children.Add(new ActivityIndicator { IsRunning = true, Color = Color.FromArgb("#0D6EFD"), HorizontalOptions = LayoutOptions.Center, Margin = new Thickness(0, 50) });
|
||
content.Children.Add(new ScrollView { Content = container });
|
||
|
||
SetContent(content);
|
||
_ = LoadRequestsAsync(container);
|
||
}
|
||
|
||
private async Task LoadRequestsAsync(VerticalStackLayout container)
|
||
{
|
||
try
|
||
{
|
||
var response = await ServiceProvider.ApiService.GetRequestsPaginatedAsync();
|
||
var requests = response?.Results;
|
||
|
||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||
{
|
||
container.Children.Clear();
|
||
|
||
if (requests == null || !requests.Any())
|
||
{
|
||
container.Children.Add(new Label { Text = "Нет заявлений", TextColor = Color.FromArgb("#888888"), HorizontalOptions = LayoutOptions.Center });
|
||
return;
|
||
}
|
||
|
||
foreach (var req in requests)
|
||
{
|
||
var statusColor = req.Status switch
|
||
{
|
||
"approved" => Color.FromArgb("#28A745"),
|
||
"rejected" => Color.FromArgb("#DC3545"),
|
||
_ => Color.FromArgb("#FFC107")
|
||
};
|
||
|
||
var card = new Border
|
||
{
|
||
BackgroundColor = Colors.White,
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 8 },
|
||
Stroke = Colors.Transparent,
|
||
Padding = new Thickness(15),
|
||
Margin = new Thickness(0, 0, 0, 5)
|
||
};
|
||
|
||
var info = new VerticalStackLayout { Spacing = 5 };
|
||
info.Children.Add(new Label { Text = req.DisplayTitle ?? req.Title, FontSize = 16, FontAttributes = FontAttributes.Bold, TextColor = Color.FromArgb("#333333") });
|
||
info.Children.Add(new Label { Text = req.Employee?.FullName ?? "", FontSize = 13, TextColor = Color.FromArgb("#666666") });
|
||
|
||
var statusLabel = new Label { Text = req.DisplayStatus ?? req.Status, FontSize = 12, TextColor = statusColor };
|
||
info.Children.Add(statusLabel);
|
||
|
||
card.Content = info;
|
||
container.Children.Add(card);
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||
{
|
||
container.Children.Clear();
|
||
container.Children.Add(new Label { Text = $"Ошибка: {ex.Message}", TextColor = Colors.Red });
|
||
});
|
||
}
|
||
}
|
||
|
||
private void LoadChatsContent()
|
||
{
|
||
var content = new VerticalStackLayout { Spacing = 15 };
|
||
content.Children.Add(CreatePageHeader("Чаты"));
|
||
|
||
var placeholder = new VerticalStackLayout
|
||
{
|
||
HorizontalOptions = LayoutOptions.Center,
|
||
VerticalOptions = LayoutOptions.Center,
|
||
Margin = new Thickness(0, 100)
|
||
};
|
||
placeholder.Children.Add(new Label { Text = "💬", FontSize = 48, HorizontalOptions = LayoutOptions.Center });
|
||
placeholder.Children.Add(new Label { Text = "Раздел чатов находится в разработке", FontSize = 16, TextColor = Color.FromArgb("#888888"), HorizontalOptions = LayoutOptions.Center });
|
||
|
||
content.Children.Add(placeholder);
|
||
SetContent(content);
|
||
}
|
||
|
||
private void LoadDocumentsContent()
|
||
{
|
||
var content = new VerticalStackLayout { Spacing = 15 };
|
||
content.Children.Add(CreatePageHeader("Документы"));
|
||
|
||
var container = new VerticalStackLayout { Spacing = 10 };
|
||
container.Children.Add(new ActivityIndicator { IsRunning = true, Color = Color.FromArgb("#0D6EFD"), HorizontalOptions = LayoutOptions.Center, Margin = new Thickness(0, 50) });
|
||
content.Children.Add(new ScrollView { Content = container });
|
||
|
||
SetContent(content);
|
||
_ = LoadDocumentsAsync(container);
|
||
}
|
||
|
||
private async Task LoadDocumentsAsync(VerticalStackLayout container)
|
||
{
|
||
try
|
||
{
|
||
var documents = await ServiceProvider.ApiService.GetAllDocumentsAsync();
|
||
|
||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||
{
|
||
container.Children.Clear();
|
||
|
||
if (documents == null || !documents.Any())
|
||
{
|
||
var placeholder = new VerticalStackLayout
|
||
{
|
||
HorizontalOptions = LayoutOptions.Center,
|
||
VerticalOptions = LayoutOptions.Center,
|
||
Margin = new Thickness(0, 50)
|
||
};
|
||
placeholder.Children.Add(new Label { Text = "📄", FontSize = 48, HorizontalOptions = LayoutOptions.Center });
|
||
placeholder.Children.Add(new Label { Text = "Нет документов для отображения", FontSize = 16, TextColor = Color.FromArgb("#888888"), HorizontalOptions = LayoutOptions.Center });
|
||
container.Children.Add(placeholder);
|
||
return;
|
||
}
|
||
|
||
foreach (var doc in documents)
|
||
{
|
||
var statusColor = doc.IsAcknowledged
|
||
? Color.FromArgb("#28A745") // Зеленый - ознакомлен
|
||
: Color.FromArgb("#FFC107"); // Желтый - не ознакомлен
|
||
|
||
var card = new Border
|
||
{
|
||
BackgroundColor = Colors.White,
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 8 },
|
||
Stroke = Colors.Transparent,
|
||
Padding = new Thickness(15),
|
||
Margin = new Thickness(0, 0, 0, 5),
|
||
Shadow = new Shadow { Brush = new SolidColorBrush(Colors.Black), Opacity = 0.05f, Offset = new Point(0, 2), Radius = 4 }
|
||
};
|
||
|
||
var row = new Grid
|
||
{
|
||
ColumnDefinitions = new ColumnDefinitionCollection
|
||
{
|
||
new ColumnDefinition(new GridLength(50)),
|
||
new ColumnDefinition(GridLength.Star),
|
||
new ColumnDefinition(GridLength.Auto)
|
||
}
|
||
};
|
||
|
||
// Иконка документа
|
||
var iconBorder = new Border
|
||
{
|
||
WidthRequest = 40,
|
||
HeightRequest = 40,
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 8 },
|
||
BackgroundColor = Color.FromArgb("#E8F0FE"),
|
||
Stroke = Colors.Transparent,
|
||
VerticalOptions = LayoutOptions.Center
|
||
};
|
||
iconBorder.Content = new Label
|
||
{
|
||
Text = doc.FileIcon,
|
||
FontSize = 20,
|
||
HorizontalOptions = LayoutOptions.Center,
|
||
VerticalOptions = LayoutOptions.Center
|
||
};
|
||
Grid.SetColumn(iconBorder, 0);
|
||
row.Children.Add(iconBorder);
|
||
|
||
// Информация о документе
|
||
var info = new VerticalStackLayout { Spacing = 3, VerticalOptions = LayoutOptions.Center };
|
||
info.Children.Add(new Label { Text = doc.Title, FontSize = 15, FontAttributes = FontAttributes.Bold, TextColor = Color.FromArgb("#333333"), LineBreakMode = LineBreakMode.TailTruncation, MaxLines = 1 });
|
||
info.Children.Add(new Label { Text = doc.ShortDescription, FontSize = 12, TextColor = Color.FromArgb("#666666"), LineBreakMode = LineBreakMode.TailTruncation, MaxLines = 1 });
|
||
|
||
var detailsRow = new HorizontalStackLayout { Spacing = 10 };
|
||
detailsRow.Children.Add(new Label { Text = $"👤 {doc.UploaderName}", FontSize = 11, TextColor = Color.FromArgb("#888888") });
|
||
detailsRow.Children.Add(new Label { Text = "•", FontSize = 11, TextColor = Color.FromArgb("#CCCCCC") });
|
||
detailsRow.Children.Add(new Label { Text = doc.DisplayUploadedAt, FontSize = 11, TextColor = Color.FromArgb("#888888") });
|
||
info.Children.Add(detailsRow);
|
||
|
||
Grid.SetColumn(info, 1);
|
||
row.Children.Add(info);
|
||
|
||
// Статус ознакомления
|
||
var statusBadge = new Border
|
||
{
|
||
BackgroundColor = statusColor,
|
||
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 10 },
|
||
Stroke = Colors.Transparent,
|
||
Padding = new Thickness(10, 5),
|
||
VerticalOptions = LayoutOptions.Center
|
||
};
|
||
statusBadge.Content = new Label
|
||
{
|
||
Text = doc.AcknowledgedText,
|
||
FontSize = 11,
|
||
TextColor = Colors.White
|
||
};
|
||
Grid.SetColumn(statusBadge, 2);
|
||
row.Children.Add(statusBadge);
|
||
|
||
card.Content = row;
|
||
|
||
// Обработчик нажатия - открытие документа
|
||
var tapGesture = new TapGestureRecognizer();
|
||
tapGesture.Tapped += async (s, e) =>
|
||
{
|
||
if (!string.IsNullOrEmpty(doc.FileUrl))
|
||
{
|
||
try
|
||
{
|
||
await Launcher.OpenAsync(new Uri(doc.FileUrl));
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
if (Application.Current?.Windows.Count > 0)
|
||
await Application.Current.Windows[0].Page!.DisplayAlert("Ошибка", $"Не удалось открыть документ: {ex.Message}", "OK");
|
||
}
|
||
}
|
||
};
|
||
card.GestureRecognizers.Add(tapGesture);
|
||
|
||
container.Children.Add(card);
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||
{
|
||
container.Children.Clear();
|
||
container.Children.Add(new Label { Text = $"Ошибка загрузки документов: {ex.Message}", TextColor = Colors.Red, HorizontalOptions = LayoutOptions.Center });
|
||
});
|
||
}
|
||
}
|
||
|
||
private View CreatePageHeader(string title, string? buttonText = null)
|
||
{
|
||
var header = new Grid
|
||
{
|
||
ColumnDefinitions = new ColumnDefinitionCollection
|
||
{
|
||
new ColumnDefinition(GridLength.Auto),
|
||
new ColumnDefinition(GridLength.Star),
|
||
new ColumnDefinition(GridLength.Auto)
|
||
},
|
||
Margin = new Thickness(0, 0, 0, 20)
|
||
};
|
||
|
||
var titleLabel = new Label
|
||
{
|
||
Text = title,
|
||
FontSize = 24,
|
||
FontAttributes = FontAttributes.Bold,
|
||
TextColor = Color.FromArgb("#333333"),
|
||
VerticalOptions = LayoutOptions.Center
|
||
};
|
||
|
||
Grid.SetColumn(titleLabel, 0);
|
||
header.Children.Add(titleLabel);
|
||
|
||
if (!string.IsNullOrEmpty(buttonText))
|
||
{
|
||
var addButton = new HorizontalStackLayout { Spacing = 8, VerticalOptions = LayoutOptions.Center };
|
||
addButton.Children.Add(new Label { Text = "+", FontSize = 20, TextColor = Color.FromArgb("#0D6EFD"), VerticalOptions = LayoutOptions.Center });
|
||
addButton.Children.Add(new Label { Text = buttonText, FontSize = 16, TextColor = Color.FromArgb("#0D6EFD"), VerticalOptions = LayoutOptions.Center });
|
||
Grid.SetColumn(addButton, 2);
|
||
header.Children.Add(addButton);
|
||
}
|
||
|
||
return header;
|
||
}
|
||
|
||
private INavigation? ResolveNavigation()
|
||
{
|
||
// Попробуем найти Page, обойдя родителей
|
||
Element? el = this;
|
||
while (el != null)
|
||
{
|
||
if (el is Page p)
|
||
return p.Navigation;
|
||
el = el.Parent;
|
||
}
|
||
|
||
// Application.Current.MainPage может быть Shell или NavigationPage
|
||
if (Application.Current?.MainPage?.Navigation != null)
|
||
return Application.Current.MainPage.Navigation;
|
||
|
||
// Shell.Current.Navigation
|
||
if (Shell.Current?.Navigation != null)
|
||
return Shell.Current.Navigation;
|
||
|
||
// Попробуем взять первую открытую Window
|
||
if (Application.Current?.Windows != null && Application.Current.Windows.Count > 0)
|
||
return Application.Current.Windows[0].Page?.Navigation;
|
||
|
||
return null;
|
||
}
|
||
}
|