diff --git a/src/main/java/com/dsol/pki_management/app/Launcher.java b/src/main/java/com/dsol/pki_management/app/Launcher.java new file mode 100644 index 0000000..492a204 --- /dev/null +++ b/src/main/java/com/dsol/pki_management/app/Launcher.java @@ -0,0 +1,13 @@ +package com.dsol.pki_management.app; + +import javafx.application.Application; + +/** + * Точка входа в приложение + */ +public class Launcher { + public static void main(String[] args) { + Application.launch(PKIApplication.class, args); + } +} + diff --git a/src/main/java/com/dsol/pki_management/app/PKIApplication.java b/src/main/java/com/dsol/pki_management/app/PKIApplication.java new file mode 100644 index 0000000..b5b9984 --- /dev/null +++ b/src/main/java/com/dsol/pki_management/app/PKIApplication.java @@ -0,0 +1,25 @@ +package com.dsol.pki_management.app; + +import com.dsol.pki_management.controllers.MainController; +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.stage.Stage; + +import java.io.IOException; + +/** + * Главный класс приложения PKI Management + */ +public class PKIApplication extends Application { + @Override + public void start(Stage stage) throws IOException { + FXMLLoader fxmlLoader = new FXMLLoader(PKIApplication.class.getResource("/com/dsol/pki_management/views/main.fxml")); + fxmlLoader.setController(new MainController()); + Scene scene = new Scene(fxmlLoader.load()); + stage.setTitle("PKI Management"); + stage.setScene(scene); + stage.show(); + } +} + diff --git a/src/main/java/com/dsol/pki_management/components/MenuItem.java b/src/main/java/com/dsol/pki_management/components/MenuItem.java new file mode 100644 index 0000000..fb57ec4 --- /dev/null +++ b/src/main/java/com/dsol/pki_management/components/MenuItem.java @@ -0,0 +1,122 @@ +package com.dsol.pki_management.components; + +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.geometry.Insets; +import javafx.scene.control.Label; +import javafx.scene.control.Separator; +import javafx.scene.layout.VBox; + +import java.io.IOException; +import java.util.List; + +/** + * MenuItem - визуальный блок в меню. + * Не кликабельный, используется для группировки и визуального оформления подменю. + */ +public class MenuItem extends VBox { + @FXML + private Label titleLabel; + @FXML + private Separator separator; + + private VBox subMenuContainer; + + public MenuItem() { + loadFXML(); + initializeSubMenuContainer(); + } + + /** + * Загружает FXML и применяет стили + */ + private void loadFXML() { + FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/com/dsol/pki_management/components/menu-item.fxml")); + fxmlLoader.setController(this); + + try { + VBox root = fxmlLoader.load(); + this.setAlignment(root.getAlignment()); + this.setSpacing(root.getSpacing()); + this.setStyle(root.getStyle()); + this.setPadding(root.getPadding()); + this.getChildren().addAll(root.getChildren()); + } catch (IOException exception) { + throw new RuntimeException(exception); + } + } + + /** + * Инициализирует контейнер для SubMenuItem + */ + private void initializeSubMenuContainer() { + subMenuContainer = new VBox(); + subMenuContainer.setSpacing(8.0); + subMenuContainer.setPadding(new Insets(8, 0, 0, 0)); + getChildren().add(subMenuContainer); + } + + public MenuItem(String title) { + this(); + setTitle(title); + } + + public void setTitle(String title) { + if (titleLabel != null) { + titleLabel.setText(title); + } + } + + public String getTitle() { + return titleLabel != null ? titleLabel.getText() : ""; + } + + /** + * Добавляет SubMenuItem в этот блок меню + * Может принимать как один, так и несколько элементов + */ + public void addSubMenuItems(SubMenuItem... subMenuItems) { + if (subMenuItems.length > 0) { + subMenuContainer.getChildren().addAll(subMenuItems); + updateVisibility(); + } + } + + /** + * Удаляет SubMenuItem из этого блока меню + */ + public void removeSubMenuItem(SubMenuItem subMenuItem) { + subMenuContainer.getChildren().remove(subMenuItem); + } + + /** + * Очищает все SubMenuItem из этого блока меню + */ + public void clearSubMenuItems() { + subMenuContainer.getChildren().clear(); + } + + /** + * Возвращает список всех SubMenuItem в этом блоке + */ + public List getSubMenuItems() { + return subMenuContainer.getChildren().stream() + .filter(node -> node instanceof SubMenuItem) + .map(node -> (SubMenuItem) node) + .toList(); + } + + /** + * Обновляет видимость блока меню на основе видимости SubMenuItem + * Если все SubMenuItem скрыты, блок меню также скрывается + */ + public void updateVisibility() { + List subMenuItems = getSubMenuItems(); + boolean shouldBeVisible = subMenuItems.isEmpty() || + subMenuItems.stream().anyMatch(SubMenuItem::isItemVisible); + + this.setVisible(shouldBeVisible); + this.setManaged(shouldBeVisible); + } +} + diff --git a/src/main/java/com/dsol/pki_management/components/SubMenuItem.java b/src/main/java/com/dsol/pki_management/components/SubMenuItem.java new file mode 100644 index 0000000..38258a4 --- /dev/null +++ b/src/main/java/com/dsol/pki_management/components/SubMenuItem.java @@ -0,0 +1,81 @@ +package com.dsol.pki_management.components; + +import javafx.fxml.FXMLLoader; +import javafx.scene.control.Button; +import javafx.scene.input.MouseEvent; + +import java.io.IOException; +import java.util.function.Consumer; + +/** + * SubMenuItem - кликабельная ссылка для открытия функционала. + */ +public class SubMenuItem extends Button { + private Consumer onClickHandler; + private boolean itemVisible = true; // По умолчанию видимый + + public SubMenuItem() { + loadFXML(); + setOnAction(e -> { + if (onClickHandler != null) { + onClickHandler.accept(null); + } + }); + } + + public SubMenuItem(String text) { + this(); + setText(text); + } + + public SubMenuItem(String text, Consumer onClickHandler) { + this(); + setText(text); + this.onClickHandler = onClickHandler; + } + + /** + * Загружает FXML и применяет стили + */ + private void loadFXML() { + FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/com/dsol/pki_management/components/sub-menu-item.fxml")); + fxmlLoader.setController(this); + + try { + Button root = fxmlLoader.load(); + this.setMnemonicParsing(root.isMnemonicParsing()); + this.setMaxWidth(root.getMaxWidth()); + this.setStyle(root.getStyle()); + this.setText(root.getText()); + } catch (IOException exception) { + throw new RuntimeException(exception); + } + } + + public void setOnClickHandler(Consumer handler) { + this.onClickHandler = handler; + } + + public Consumer getOnClickHandler() { + return onClickHandler; + } + + /** + * Устанавливает видимость элемента меню + * @param visible true - элемент видим, false - элемент скрыт + */ + public void setItemVisible(boolean visible) { + this.itemVisible = visible; + super.setVisible(visible); + super.setManaged(visible); + } + + /** + * Проверяет, видим ли элемент меню + * @return true если элемент видим, false если скрыт + */ + public boolean isItemVisible() { + return itemVisible; + } +} + diff --git a/src/main/java/com/dsol/pki_management/config/AppConfig.java b/src/main/java/com/dsol/pki_management/config/AppConfig.java new file mode 100644 index 0000000..bcac8c5 --- /dev/null +++ b/src/main/java/com/dsol/pki_management/config/AppConfig.java @@ -0,0 +1,27 @@ +package com.dsol.pki_management.config; + +/** + * Константы и ключи для конфигурации приложения + */ +public class AppConfig { + // Ключи конфигурации + public static final String KEY_ADMIN_PASSWORD = "admin.password"; + public static final String KEY_LANGUAGE = "app.language"; + public static final String KEY_THEME = "app.theme"; + public static final String KEY_WINDOW_WIDTH = "window.width"; + public static final String KEY_WINDOW_HEIGHT = "window.height"; + public static final String KEY_WINDOW_X = "window.x"; + public static final String KEY_WINDOW_Y = "window.y"; + + // Значения по умолчанию + public static final String DEFAULT_ADMIN_PASSWORD = "admin123"; + public static final String DEFAULT_LANGUAGE = "ru"; + public static final String DEFAULT_THEME = "light"; + public static final String DEFAULT_WINDOW_WIDTH = "960"; + public static final String DEFAULT_WINDOW_HEIGHT = "640"; + + private AppConfig() { + // Утилитный класс, не должен быть инстанциирован + } +} + diff --git a/src/main/java/com/dsol/pki_management/config/ConfigManager.java b/src/main/java/com/dsol/pki_management/config/ConfigManager.java new file mode 100644 index 0000000..9d80d99 --- /dev/null +++ b/src/main/java/com/dsol/pki_management/config/ConfigManager.java @@ -0,0 +1,180 @@ +package com.dsol.pki_management.config; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; +import java.util.logging.Logger; + +/** + * Менеджер конфигурации приложения + * Хранит конфигурацию в скрытой папке пользователя + */ +public class ConfigManager { + private static final Logger logger = Logger.getLogger(ConfigManager.class.getName()); + private static final String CONFIG_DIR_NAME = "pki_management"; + private static final String CONFIG_FILE_NAME = "config.properties"; + + private static ConfigManager instance; + private final Path configDir; + private final Path configFile; + private final Properties properties; + + private ConfigManager() { + String userHome = System.getProperty("user.home"); + this.configDir = Paths.get(userHome, CONFIG_DIR_NAME); + this.configFile = configDir.resolve(CONFIG_FILE_NAME); + this.properties = new Properties(); + + initializeConfigDirectory(); + loadConfig(); + } + + /** + * Получает единственный экземпляр ConfigManager + */ + public static synchronized ConfigManager getInstance() { + if (instance == null) { + instance = new ConfigManager(); + } + return instance; + } + + /** + * Инициализирует директорию конфигурации и делает её скрытой + */ + private void initializeConfigDirectory() { + try { + // Создаем директорию, если её нет + if (!Files.exists(configDir)) { + Files.createDirectories(configDir); + logger.info("Создана директория конфигурации: " + configDir); + } + + // Делаем директорию скрытой + setHiddenAttribute(configDir, true); + + // Создаем файл конфигурации, если его нет + if (!Files.exists(configFile)) { + Files.createFile(configFile); + logger.info("Создан файл конфигурации: " + configFile); + } + + // Делаем файл скрытым + setHiddenAttribute(configFile, true); + + } catch (IOException e) { + logger.severe("Ошибка при инициализации директории конфигурации: " + e.getMessage()); + e.printStackTrace(); + } + } + + /** + * Устанавливает атрибут скрытого файла/папки + * Для Windows использует DOS атрибут, для Unix-подобных систем - точку в начале имени + */ + private void setHiddenAttribute(Path path, boolean hidden) { + try { + if (isWindows()) { + // Для Windows используем DOS атрибут + Files.setAttribute(path, "dos:hidden", hidden); + logger.info("Атрибут 'hidden' установлен для: " + path); + } else { + // Для Unix-подобных систем файлы с точкой в начале автоматически скрыты + // Но так как пользователь указал конкретное имя, просто логируем + logger.info("В Unix-подобных системах файл будет виден. Для скрытия используйте имя, начинающееся с точки."); + } + } catch (IOException e) { + logger.warning("Не удалось установить атрибут скрытого файла/папки для " + path + ": " + e.getMessage()); + } catch (UnsupportedOperationException e) { + logger.warning("Операция установки атрибута не поддерживается для " + path + ": " + e.getMessage()); + } + } + + /** + * Загружает конфигурацию из файла + */ + public void loadConfig() { + try (InputStream input = Files.newInputStream(configFile)) { + properties.load(input); + logger.info("Конфигурация загружена из файла"); + } catch (FileNotFoundException e) { + logger.info("Файл конфигурации не найден, будет создан новый"); + saveConfig(); // Создаем файл с дефолтными значениями + } catch (IOException e) { + logger.severe("Ошибка при загрузке конфигурации: " + e.getMessage()); + e.printStackTrace(); + } + } + + /** + * Сохраняет конфигурацию в файл + */ + public void saveConfig() { + try (OutputStream output = Files.newOutputStream(configFile)) { + properties.store(output, "PKI Management Configuration"); + logger.info("Конфигурация сохранена в файл"); + } catch (IOException e) { + logger.severe("Ошибка при сохранении конфигурации: " + e.getMessage()); + e.printStackTrace(); + } + } + + /** + * Получает значение свойства + */ + public String getProperty(String key) { + return properties.getProperty(key); + } + + /** + * Получает значение свойства с дефолтным значением + */ + public String getProperty(String key, String defaultValue) { + return properties.getProperty(key, defaultValue); + } + + /** + * Устанавливает значение свойства + */ + public void setProperty(String key, String value) { + properties.setProperty(key, value); + } + + /** + * Удаляет свойство + */ + public void removeProperty(String key) { + properties.remove(key); + } + + /** + * Проверяет, существует ли свойство + */ + public boolean hasProperty(String key) { + return properties.containsKey(key); + } + + /** + * Получает путь к директории конфигурации + */ + public Path getConfigDir() { + return configDir; + } + + /** + * Получает путь к файлу конфигурации + */ + public Path getConfigFile() { + return configFile; + } + + /** + * Проверяет, является ли операционная система Windows + */ + private boolean isWindows() { + return System.getProperty("os.name").toLowerCase().contains("windows"); + } +} + diff --git a/src/main/java/com/dsol/pki_management/controllers/MainController.java b/src/main/java/com/dsol/pki_management/controllers/MainController.java new file mode 100644 index 0000000..7cbed6a --- /dev/null +++ b/src/main/java/com/dsol/pki_management/controllers/MainController.java @@ -0,0 +1,167 @@ +package com.dsol.pki_management.controllers; + +import com.dsol.pki_management.components.MenuItem; +import com.dsol.pki_management.components.SubMenuItem; +import com.dsol.pki_management.modules.test1.Test1Controller; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.control.Button; +import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import javafx.scene.input.MouseEvent; + +/** + * Главный контроллер приложения + */ +public class MainController { + @FXML + private Button hamburgerButton; + @FXML + private VBox menuPane; + @FXML + private Pane menuOverlay; + @FXML + private VBox menuContent; + @FXML + private StackPane contentArea; + + // Статический список всех SubMenuItem для доступа из других контроллеров + private static final List allSubMenuItems = new ArrayList<>(); + private static final List allMenuItems = new ArrayList<>(); + + @FXML + public void initialize() { + hamburgerButton.setOnAction(e -> openMenu()); + menuOverlay.setOnMouseClicked(e -> closeMenu()); + + setupMenuItems(); + } + + private void setupMenuItems() { + // Очищаем списки при повторной инициализации + allSubMenuItems.clear(); + allMenuItems.clear(); + + // Определяем структуру меню + MenuItem mainFunctions = createMenuItem("Основные функции", + createSubMenuItem("Главная", e -> System.out.println("Открыта главная страница")), + createSubMenuItem("Мониторинг", e -> System.out.println("Открыт мониторинг")) + ); + + MenuItem management = createMenuItem("Управление", + createSubMenuItem("Сертификаты", e -> System.out.println("Открыто управление сертификатами")), + createSubMenuItem("Отчеты", e -> System.out.println("Открыты отчеты")) + ); + + MenuItem testMenu = createMenuItem("Test", + createSubMenuItem("Тест 1", e -> { + loadTest1Module(); + closeMenu(); + }) + ); + + // Добавляем блоки меню в контент меню + if (menuContent != null) { + menuContent.getChildren().addAll(mainFunctions, management, testMenu); + } + } + + /** + * Создает MenuItem с указанным названием и подменю + */ + private MenuItem createMenuItem(String title, SubMenuItem... subMenuItems) { + MenuItem menuItem = new MenuItem(title); + allMenuItems.add(menuItem); + + if (subMenuItems.length > 0) { + menuItem.addSubMenuItems(subMenuItems); + for (SubMenuItem subMenuItem : subMenuItems) { + allSubMenuItems.add(subMenuItem); + } + } + + return menuItem; + } + + /** + * Создает SubMenuItem с указанным текстом и обработчиком + */ + private SubMenuItem createSubMenuItem(String text, Consumer onClickHandler) { + return new SubMenuItem(text, onClickHandler); + } + + /** + * Возвращает список всех SubMenuItem для доступа из других контроллеров + */ + public static List getAllSubMenuItems() { + return new ArrayList<>(allSubMenuItems); + } + + /** + * Возвращает список всех MenuItem для доступа из других контроллеров + */ + public static List getAllMenuItems() { + return new ArrayList<>(allMenuItems); + } + + @FXML + private void closeMenu() { + menuPane.setVisible(false); + menuPane.setManaged(false); + menuOverlay.setVisible(false); + menuOverlay.setManaged(false); + } + + private void openMenu() { + menuPane.setVisible(true); + menuPane.setManaged(true); + menuOverlay.setVisible(true); + menuOverlay.setManaged(true); + } + + /** + * Загружает модуль "Тест 1" в центральную область + */ + private void loadTest1Module() { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/dsol/pki_management/modules/test1/test1-view.fxml")); + VBox test1View = loader.load(); + + if (contentArea != null) { + contentArea.getChildren().clear(); + contentArea.getChildren().add(test1View); + } + } catch (IOException e) { + System.err.println("Ошибка загрузки модуля Тест 1: " + e.getMessage()); + e.printStackTrace(); + } + } + + /** + * Загружает модуль настроек в центральную область + */ + @FXML + private void loadSettingsModule() { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/dsol/pki_management/modules/settings/settings-view.fxml")); + VBox settingsView = loader.load(); + + if (contentArea != null) { + contentArea.getChildren().clear(); + contentArea.getChildren().add(settingsView); + } + + closeMenu(); + } catch (IOException e) { + System.err.println("Ошибка загрузки модуля настроек: " + e.getMessage()); + e.printStackTrace(); + } + } +} + diff --git a/src/main/java/com/dsol/pki_management/controllers/SettingsController.java b/src/main/java/com/dsol/pki_management/controllers/SettingsController.java new file mode 100644 index 0000000..0760057 --- /dev/null +++ b/src/main/java/com/dsol/pki_management/controllers/SettingsController.java @@ -0,0 +1,241 @@ +package com.dsol.pki_management.controllers; + +import com.dsol.pki_management.components.MenuItem; +import com.dsol.pki_management.components.SubMenuItem; +import com.dsol.pki_management.config.AppConfig; +import com.dsol.pki_management.config.ConfigManager; +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.geometry.Insets; +import javafx.scene.control.*; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.stage.Modality; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Контроллер для модуля настроек + */ +public class SettingsController { + @FXML + private VBox userSettingsContainer; + @FXML + private VBox adminSettingsContainer; + @FXML + private Button adminAccessButton; + @FXML + private Button exitAdminButton; + @FXML + private Label statusLabel; + @FXML + private VBox subMenuItemsContainer; + + private boolean isAdminMode = false; + private final ConfigManager configManager = ConfigManager.getInstance(); + private Map subMenuItemTexts = new HashMap<>(); + + // Константы стилей + private static final String STYLE_MENU_ITEM_CONTAINER = "-fx-background-color: #e9ecef; -fx-background-radius: 4;"; + private static final String STYLE_MENU_ITEM_LABEL = "-fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: #212529;"; + private static final String STYLE_SUB_ITEM_VISIBLE = "-fx-font-size: 14px; -fx-fill: #495057; -fx-cursor: hand;"; + private static final String STYLE_SUB_ITEM_HIDDEN = "-fx-font-size: 14px; -fx-fill: #dc3545; -fx-cursor: hand;"; + + @FXML + public void initialize() { + setContainerVisibility(userSettingsContainer, true); + setContainerVisibility(adminSettingsContainer, false); + + if (adminAccessButton != null) { + adminAccessButton.setOnAction(e -> showAdminPasswordDialog()); + } + if (exitAdminButton != null) { + exitAdminButton.setOnAction(e -> disableAdminMode()); + } + } + + /** + * Устанавливает видимость контейнера + */ + private void setContainerVisibility(VBox container, boolean visible) { + container.setVisible(visible); + container.setManaged(visible); + } + + /** + * Показывает диалог для ввода пароля администратора + */ + private void showAdminPasswordDialog() { + PasswordField passwordField = new PasswordField(); + passwordField.setPromptText("Пароль"); + + VBox content = new VBox(10); + content.getChildren().addAll(new Label("Пароль:"), passwordField); + content.setPadding(new Insets(20)); + + Dialog dialog = new Dialog<>(); + dialog.setTitle("Админский доступ"); + dialog.setHeaderText("Введите пароль администратора"); + dialog.initModality(Modality.APPLICATION_MODAL); + dialog.getDialogPane().setContent(content); + + ButtonType loginButtonType = new ButtonType("Войти", ButtonBar.ButtonData.OK_DONE); + dialog.getDialogPane().getButtonTypes().addAll(loginButtonType, ButtonType.CANCEL); + ((Button) dialog.getDialogPane().lookupButton(loginButtonType)).setDefaultButton(true); + + dialog.setResultConverter(button -> button == loginButtonType ? passwordField.getText() : null); + dialog.setOnShown(e -> Platform.runLater(passwordField::requestFocus)); + + dialog.showAndWait().ifPresent(password -> { + String adminPassword = configManager.getProperty(AppConfig.KEY_ADMIN_PASSWORD, AppConfig.DEFAULT_ADMIN_PASSWORD); + if (adminPassword.equals(password)) { + enableAdminMode(); + } else { + showErrorDialog("Неверный пароль", "Введен неверный пароль. Доступ запрещен."); + } + }); + } + + /** + * Включает режим администратора + */ + private void enableAdminMode() { + isAdminMode = true; + setContainerVisibility(userSettingsContainer, false); + setContainerVisibility(adminSettingsContainer, true); + + if (statusLabel != null) { + statusLabel.setText("Режим администратора активен"); + statusLabel.setStyle("-fx-text-fill: green;"); + } + + loadSubMenuItemsControls(); + } + + /** + * Загружает дерево для управления видимостью SubMenuItem + */ + private void loadSubMenuItemsControls() { + if (subMenuItemsContainer == null) { + return; + } + + // Очищаем контейнер + subMenuItemsContainer.getChildren().clear(); + subMenuItemTexts.clear(); + + // Добавляем отступ для всего блока + subMenuItemsContainer.setPadding(new Insets(15, 0, 15, 0)); + subMenuItemsContainer.setSpacing(8); + + // Получаем все MenuItem и SubMenuItem из HelloController + List menuItems = MainController.getAllMenuItems(); + + if (menuItems.isEmpty()) { + Label noItemsLabel = new Label("Нет элементов меню для управления"); + noItemsLabel.setStyle("-fx-text-fill: #6c757d;"); + subMenuItemsContainer.getChildren().add(noItemsLabel); + return; + } + + // Создаем дерево: для каждого MenuItem показываем заголовок и его SubMenuItem + for (MenuItem menuItem : menuItems) { + subMenuItemsContainer.getChildren().add(createMenuItemHeader(menuItem.getTitle())); + + for (SubMenuItem subMenuItem : menuItem.getSubMenuItems()) { + Text subItemText = createSubMenuItemText(subMenuItem); + subMenuItemTexts.put(subMenuItem, subItemText); + subMenuItemsContainer.getChildren().add(createSubMenuItemContainer(subItemText)); + } + } + } + + /** + * Создает заголовок MenuItem + */ + private HBox createMenuItemHeader(String title) { + HBox container = new HBox(); + container.setPadding(new Insets(8, 12, 8, 12)); + container.setStyle(STYLE_MENU_ITEM_CONTAINER); + + Label label = new Label("📁 " + title); + label.setStyle(STYLE_MENU_ITEM_LABEL); + container.getChildren().add(label); + + return container; + } + + /** + * Создает контейнер для SubMenuItem с отступом + */ + private HBox createSubMenuItemContainer(Text text) { + HBox container = new HBox(); + container.setPadding(new Insets(4, 0, 4, 30)); + container.getChildren().add(text); + return container; + } + + /** + * Создает кликабельный Text для SubMenuItem + */ + private Text createSubMenuItemText(SubMenuItem subMenuItem) { + Text text = new Text(" • " + subMenuItem.getText()); + + // Устанавливаем стиль в зависимости от видимости + updateSubMenuItemTextStyle(text, subMenuItem.isItemVisible()); + + // Делаем Text кликабельным + text.setStyle(text.getStyle() + " -fx-cursor: hand;"); + text.setOnMouseClicked(e -> toggleSubMenuItemVisibility(subMenuItem, text)); + + return text; + } + + /** + * Переключает видимость SubMenuItem при клике + */ + private void toggleSubMenuItemVisibility(SubMenuItem subMenuItem, Text text) { + boolean newVisibility = !subMenuItem.isItemVisible(); + subMenuItem.setItemVisible(newVisibility); + updateSubMenuItemTextStyle(text, newVisibility); + + // Обновляем видимость всех MenuItem + MainController.getAllMenuItems().forEach(MenuItem::updateVisibility); + } + + /** + * Обновляет стиль Text в зависимости от видимости + */ + private void updateSubMenuItemTextStyle(Text text, boolean isVisible) { + text.setStyle(isVisible ? STYLE_SUB_ITEM_VISIBLE : STYLE_SUB_ITEM_HIDDEN); + text.setStrikethrough(!isVisible); + } + + /** + * Отключает режим администратора + */ + @FXML + private void disableAdminMode() { + isAdminMode = false; + setContainerVisibility(userSettingsContainer, true); + setContainerVisibility(adminSettingsContainer, false); + + if (statusLabel != null) { + statusLabel.setText(""); + } + } + + /** + * Показывает диалог с ошибкой + */ + private void showErrorDialog(String title, String message) { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle(title); + alert.setHeaderText(null); + alert.setContentText(message); + alert.showAndWait(); + } +} + diff --git a/src/main/java/com/dsol/pki_management/modules/test1/Test1Controller.java b/src/main/java/com/dsol/pki_management/modules/test1/Test1Controller.java new file mode 100644 index 0000000..e2aa777 --- /dev/null +++ b/src/main/java/com/dsol/pki_management/modules/test1/Test1Controller.java @@ -0,0 +1,23 @@ +package com.dsol.pki_management.modules.test1; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.VBox; + +/** + * Контроллер для модуля "Тест 1" + */ +public class Test1Controller { + @FXML + private VBox root; + @FXML + private Label titleLabel; + + @FXML + public void initialize() { + if (titleLabel != null) { + titleLabel.setText("Модуль Тест 1"); + } + } +} + diff --git a/src/main/resources/com/dsol/pki_management/components/menu-item.fxml b/src/main/resources/com/dsol/pki_management/components/menu-item.fxml new file mode 100644 index 0000000..b2f2899 --- /dev/null +++ b/src/main/resources/com/dsol/pki_management/components/menu-item.fxml @@ -0,0 +1,22 @@ + + + + + + + + + + + + diff --git a/src/main/resources/com/dsol/pki_management/components/sub-menu-item.fxml b/src/main/resources/com/dsol/pki_management/components/sub-menu-item.fxml new file mode 100644 index 0000000..c10b692 --- /dev/null +++ b/src/main/resources/com/dsol/pki_management/components/sub-menu-item.fxml @@ -0,0 +1,10 @@ + + + + + + Мой сайт + + + + +
+
+
Навигация
+ +
+ + + +
+ + + +