using Npgsql; using TravelAgencyWinForms.Data; using TravelAgencyWinForms.Models; using TravelAgencyWinForms.Services; using TravelAgencyWinForms.Utils; namespace TravelAgencyWinForms.Forms; public partial class MainForm : Form { private Button? _activeMenuButton; public MainForm() { InitializeComponent(); ApplyStyle(); LoadBanner(); BuildMenu(); LoadDashboard(); FormClosing += (_, _) => CurrentUser.Clear(); } private void ApplyStyle() { AppTheme.ApplyForm(this); menuPanel.BackColor = Color.FromArgb(76, 150, 138); menuHeaderPanel.BackColor = Color.FromArgb(76, 150, 138); menuItemsPanel.BackColor = Color.FromArgb(76, 150, 138); appTitleLabel.ForeColor = Color.White; modeLabel.ForeColor = Color.FromArgb(227, 247, 242); loginLabel.ForeColor = Color.FromArgb(227, 247, 242); userNameLabel.ForeColor = Color.White; contentPanel.BackColor = AppTheme.Background; heroPanel.BackColor = AppTheme.Surface; heroTitleLabel.ForeColor = AppTheme.Text; heroSubtitleLabel.ForeColor = AppTheme.Muted; upcomingGroupBox.ForeColor = AppTheme.Text; bookingsGroupBox.ForeColor = AppTheme.Text; AppTheme.StyleGrid(upcomingGrid); AppTheme.StyleGrid(bookingsGrid); appTitleLabel.Text = "Турагентство"; modeLabel.Text = CurrentUser.IsAdmin ? "Панель администратора" : "Личный кабинет клиента"; loginLabel.Text = CurrentUser.Login; userNameLabel.Text = string.IsNullOrWhiteSpace(CurrentUser.ClientName) ? CurrentUser.Login : CurrentUser.ClientName; heroTitleLabel.Text = CurrentUser.IsAdmin ? "Панель управления" : "Личный кабинет"; heroSubtitleLabel.Text = CurrentUser.IsAdmin ? "Быстрый доступ к данным, отчётам и журналу операций" : "Просмотр туров, расписания и своих бронирований"; } private void LoadBanner() { var image = AppTheme.LoadImage("main_dashboard_banner.png"); if (image != null) bannerPictureBox.Image = image; } private void BuildMenu() { menuItemsPanel.Controls.Clear(); AddMenuButton("Главная", LoadDashboard, true); if (CurrentUser.IsAdmin) { AddSection("Данные"); AddMenuButton("Клиенты", () => OpenTable(EntityConfigs.Clients, true)); AddMenuButton("Должности", () => OpenTable(EntityConfigs.Positions, true)); AddMenuButton("Сотрудники", () => OpenTable(EntityConfigs.Employees, true)); AddMenuButton("Города", () => OpenTable(EntityConfigs.Cities, true)); AddMenuButton("Туры", () => OpenTable(EntityConfigs.Tours, true)); AddMenuButton("Маршруты", () => OpenTable(EntityConfigs.TourCities, true)); AddMenuButton("Расписание", () => OpenTable(EntityConfigs.TourList, true)); AddMenuButton("Бронирования", () => OpenTable(EntityConfigs.Bookings, true)); AddSection("Отчёты"); AddMenuButton("Сводка", () => new ReportsForm().ShowDialog(this)); AddMenuButton("Просроченные", () => OpenTable(EntityConfigs.OverdueView, false)); AddMenuButton("Журнал", () => OpenTable(EntityConfigs.AuditLog, false)); AddSection("Сервис"); AddMenuButton("Пользователи", () => OpenTable(EntityConfigs.AppUsers, true)); } else { AddSection("Мои данные"); AddMenuButton("Личные данные", () => OpenTable(EntityConfigs.ClientDetails, false)); AddMenuButton("Бронирования", () => OpenTable(EntityConfigs.UserBookings, false)); AddMenuButton("Просроченные", () => OpenTable(EntityConfigs.UserOverdue, false)); AddSection("Справочники"); AddMenuButton("Города", () => OpenTable(EntityConfigs.Cities, false)); AddMenuButton("Туры", () => OpenTable(EntityConfigs.Tours, false)); AddMenuButton("Расписание", () => OpenTable(EntityConfigs.TourScheduleView, false)); AddMenuButton("Отчёты", () => new ReportsForm().ShowDialog(this)); } AddSection(string.Empty); AddMenuButton("Выход", Close); } private void AddMenuButton(string text, Action action, bool selected = false) { var button = new Button { Text = text, Width = 220, Height = 38, Margin = new Padding(0, 3, 0, 3) }; AppTheme.StyleMenuButton(button); if (selected) SetActiveMenuButton(button); button.Click += (_, _) => { SetActiveMenuButton(button); action(); }; menuItemsPanel.Controls.Add(button); } private void SetActiveMenuButton(Button button) { if (_activeMenuButton != null) AppTheme.StyleMenuButton(_activeMenuButton); _activeMenuButton = button; button.BackColor = Color.FromArgb(98, 181, 166); } private void AddSection(string text) { if (string.IsNullOrWhiteSpace(text)) { menuItemsPanel.Controls.Add(new Panel { Height = 12, Width = 220 }); return; } var label = new Label { Text = text.ToUpperInvariant(), Width = 220, Height = 26, ForeColor = Color.FromArgb(216, 243, 237), Font = new Font("Segoe UI Semibold", 8.5F), Margin = new Padding(0, 14, 0, 2) }; menuItemsPanel.Controls.Add(label); } private void OpenTable(EntityConfig config, bool canEdit) { using var form = new TableEditorForm(config, canEdit); form.ShowDialog(this); LoadDashboard(); } private void LoadDashboard() { LoadStats(); LoadDashboardTables(); } private void LoadStats() { statsPanel.Controls.Clear(); foreach (var card in LoadStatCards()) statsPanel.Controls.Add(card); } private IEnumerable LoadStatCards() { if (CurrentUser.IsAdmin) { yield return AppTheme.CreateCard("Клиенты", ScalarInt("SELECT COUNT(*) FROM clients")); yield return AppTheme.CreateCard("Сотрудники", ScalarInt("SELECT COUNT(*) FROM employees")); yield return AppTheme.CreateCard("Туры", ScalarInt("SELECT COUNT(*) FROM tours")); yield return AppTheme.CreateCard("Бронирования", ScalarInt("SELECT COUNT(*) FROM bookings")); yield return AppTheme.CreateCard("Просроченные", ScalarInt("SELECT COUNT(*) FROM v_overdue_client_bookings")); } else { yield return AppTheme.CreateCard("Бронирования", ScalarInt("SELECT COUNT(*) FROM bookings WHERE client_id = @client_id", new NpgsqlParameter("client_id", CurrentUser.ClientId ?? -1))); yield return AppTheme.CreateCard("Просроченные", ScalarInt("SELECT COUNT(*) FROM v_overdue_client_bookings WHERE client_id = @client_id", new NpgsqlParameter("client_id", CurrentUser.ClientId ?? -1))); yield return AppTheme.CreateCard("Туры", ScalarInt("SELECT COUNT(*) FROM tours")); yield return AppTheme.CreateCard("Рейсы", ScalarInt("SELECT COUNT(*) FROM tour_list WHERE start_date >= CURRENT_DATE"), "будущие даты"); } } private void LoadDashboardTables() { upcomingGrid.DataSource = Db.Query(@" SELECT tour_name AS ""Тур"", start_date AS ""Дата начала"", end_date AS ""Дата окончания"", duration_days AS ""Длительность"", price AS ""Стоимость"" FROM v_tour_schedule WHERE start_date >= CURRENT_DATE ORDER BY start_date LIMIT 8;"); bookingsGrid.DataSource = CurrentUser.IsAdmin ? Db.Query(@" SELECT booking_number AS ""Номер"", client_name AS ""Клиент"", employee_name AS ""Сотрудник"", tour_name AS ""Тур"", booking_date AS ""Дата оформления"", status AS ""Статус"" FROM v_booking_details ORDER BY booking_date DESC, booking_id DESC LIMIT 8;") : Db.Query(@" SELECT booking_number AS ""Номер"", client_name AS ""Клиент"", tour_name AS ""Тур"", booking_date AS ""Дата оформления"", status AS ""Статус"" FROM v_booking_details WHERE client_id = @client_id ORDER BY booking_date DESC, booking_id DESC LIMIT 8;", new NpgsqlParameter("client_id", CurrentUser.ClientId ?? -1)); AppTheme.StyleGrid(upcomingGrid); AppTheme.StyleGrid(bookingsGrid); PresentationHelper.ApplyHeaders(upcomingGrid); PresentationHelper.ApplyHeaders(bookingsGrid); } private static string ScalarInt(string sql, params NpgsqlParameter[] parameters) { var result = Db.Scalar(sql, parameters); return Convert.ToInt32(result ?? 0).ToString(); } }