Skip to content

Commit

Permalink
enums for permission name and type, also tutorial mode - based on htt…
Browse files Browse the repository at this point in the history
  • Loading branch information
HardMax71 committed Aug 28, 2024
1 parent e15a839 commit 8139662
Show file tree
Hide file tree
Showing 28 changed files with 431 additions and 112 deletions.
3 changes: 2 additions & 1 deletion desktop_app/resources/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
"templates_path": "resources/templates",
"translations_path": "resources/translations",
"organization_domain": "nexusware.com",
"show_manual_after_login": false
"show_manual_after_login": false,
"start_tutorial": false
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added desktop_app/resources/icons/tutorial_mode/close.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions desktop_app/resources/styles/tutorial_mode/highlight.qss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
* {
background-color: #FFEFD5; /* PapayaWhip for a softer highlight */
border: 2px solid #FF6347; /* Tomato for a bolder border */
color: #D2691E; /* Chocolate for a warm text color */
border-radius: 6px;
padding: 5px;
}
44 changes: 44 additions & 0 deletions desktop_app/resources/styles/tutorial_mode/tutorial_hint.qss
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* QTutorialHint frame style */
QTutorialHint {
background-color: #FFFACD;
border: 2px solid #FF8C00;
border-radius: 12px;
padding: 5px;
}

/* QLabel style within QTutorialHint */
QTutorialHint QLabel {
color: #8B0000;
font-size: 14px;
padding: 4px;
}

/* Progress label style */
QTutorialHint QLabel#progress_label {
background-color: #FFEBCD;
color: #8B0000;
font-weight: bold;
border-radius: 8px;
padding: 3px 5px;
margin-bottom: 5px;
font-size: 12px;
border: 1px solid #FF8C00;
}

/* QPushButton base style */
QPushButton {
background-color: transparent;
border: none;
padding: 0px;
border-radius: 6px;
}

/* Hover state: slightly darker background to indicate hover */
QPushButton:hover {
background-color: #FFD700; /* Gold color for hover effect */
}

/* Pressed state: darker background to indicate button press */
QPushButton:pressed {
background-color: #FFA500; /* Orange color for pressed state */
}
2 changes: 1 addition & 1 deletion desktop_app/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from services.authentication import AuthenticationService
from services.offline_manager import OfflineManager
from services.update_manager import UpdateManager
from ui.views.auth import LoginDialog
from ui.main_window import MainWindow
from ui.views.auth import LoginDialog
from utils.config_manager import ConfigManager
from utils.logger import setup_logger

Expand Down
69 changes: 34 additions & 35 deletions desktop_app/src/ui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@
from desktop_app.src.ui.views.user_mgmt.user_mgmt_widget import UserManagementWidget
from desktop_app.src.utils import ConfigManager
from public_api.api import APIClient
from public_api.permission_manager import PermissionManager
from public_api.permissions import PermissionName
from public_api.permissions.permission_manager import PermissionManager
from .components.dialogs import UserManualDialog, AboutDialog
from .dashboard import DashboardWidget
from .notification_center import NotificationCenter
from .qtutorial import QTutorialManager
from .report_generator import ReportGeneratorWidget
from .search_filter import AdvancedSearchDialog
from .settings.settings_dialog import SettingsDialog
Expand All @@ -30,11 +32,6 @@ def __init__(self, api_client: APIClient, config_manager: ConfigManager,
self.permission_manager = permission_manager
self.init_ui()

# TODO : Implement training mode, now it overlaps with the main window
# self.training_manager = TrainingModeManager(self, self.config_manager)
# QApplication.instance().processEvents() # Ensure UI is fully loaded
# self.training_manager.start_training()

def init_ui(self):
self.setWindowTitle("NexusWare WMS")
self.setWindowIcon(QIcon("icons:app_icon.png"))
Expand All @@ -56,48 +53,50 @@ def init_ui(self):

self.create_menu_bar()

def add_tabs_based_on_permissions(self):
tab_permissions = {
"Dashboard": "dashboard",
"Inventory": "inventory",
"Orders": "orders",
"Products": "products",
"Suppliers": "suppliers",
"Customers": "customers",
"Shipments": "shipments",
"Reports": "reports",
"User Management": "user_management",
"Tasks Management": "tasks_management",
}
if self.config_manager.get("start_tutorial", True):
self.initialize_tutorial_manager()

def initialize_tutorial_manager(self):
tutorial_steps = [
(self.tab_widget, "This is the main window where you can navigate between different tabs."),
(self.tab_widget.tabBar(), "Click on a tab to view the content."),
(self.status_bar, "This is the status bar where you can see notifications and other messages."),
(self.menuBar(), "Use the menu bar to access different features."),
(self.notification_center, "Click on the bell icon to view notifications."),
]

self.tutorial_manager = QTutorialManager(self, tutorial_steps, show_step_number=True)
self.tutorial_manager.start_tutorial()

def add_tabs_based_on_permissions(self):
tab_classes = {
"Dashboard": DashboardWidget,
"Inventory": InventoryView,
"Orders": OrderView,
"Products": ProductView,
"Suppliers": SupplierView,
"Customers": CustomerView,
"Shipments": ShipmentView,
"Reports": ReportGeneratorWidget,
"User Management": UserManagementWidget,
"Tasks Management": TaskView,
PermissionName.DASHBOARD: DashboardWidget,
PermissionName.INVENTORY: InventoryView,
PermissionName.ORDERS: OrderView,
PermissionName.PRODUCTS: ProductView,
PermissionName.SUPPLIERS: SupplierView,
PermissionName.CUSTOMERS: CustomerView,
PermissionName.SHIPMENTS: ShipmentView,
PermissionName.REPORTS: ReportGeneratorWidget,
PermissionName.USER_MANAGEMENT: UserManagementWidget,
PermissionName.TASKS_MANAGEMENT: TaskView,
}

for tab_name, permission_name in tab_permissions.items():
if self.permission_manager.has_read_permission(permission_name):
tab_widget = tab_classes[tab_name](self.api_client)
self.tab_widget.addTab(tab_widget, tab_name)
for tab_name, tab_class in tab_classes.items():
if self.permission_manager.has_read_permission(tab_name):
tab_widget = tab_class(self.api_client)
self.tab_widget.addTab(tab_widget, tab_name.value)

def create_menu_bar(self):
menu_bar = self.menuBar()

file_menu = menu_bar.addMenu("File")
if self.permission_manager.has_read_permission("settings"):
if self.permission_manager.has_read_permission(PermissionName.SETTINGS):
file_menu.addAction("Settings", self.open_settings)
file_menu.addAction("Exit", self.close)

view_menu = menu_bar.addMenu("View")
if self.permission_manager.has_read_permission("adv_search"):
if self.permission_manager.has_read_permission(PermissionName.ADVANCED_SEARCH):
view_menu.addAction("Advanced Search", self.open_advanced_search)

help_menu = menu_bar.addMenu("Help")
Expand Down
3 changes: 3 additions & 0 deletions desktop_app/src/ui/qtutorial/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .hint import QTutorialHint
from .tutorial_manager import QTutorialManager
from .utils import load_stylesheet
99 changes: 99 additions & 0 deletions desktop_app/src/ui/qtutorial/hint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from PySide6.QtCore import Qt, QRect, QPoint
from PySide6.QtGui import QColor, QPainter, QIcon
from PySide6.QtWidgets import (QApplication, QLabel, QPushButton,
QVBoxLayout, QHBoxLayout,
QFrame)

from .utils import load_stylesheet


class QTutorialHint(QFrame):
def __init__(self, text, current_step, total_steps, show_step_number=True, parent=None):
super().__init__(parent)
self.setWindowFlags(Qt.Tool | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setStyleSheet(load_stylesheet('styles:tutorial_mode/tutorial_hint.qss'))
self.setFocusPolicy(Qt.StrongFocus)

layout = QVBoxLayout(self)

if show_step_number:
self.progress_label = QLabel(f"Step {current_step + 1} of {total_steps}")
self.progress_label.setAlignment(Qt.AlignCenter)
self.progress_label.setObjectName("progress_label")
layout.addWidget(self.progress_label)

hint_text = QLabel(text)
hint_text.setWordWrap(True)
layout.addWidget(hint_text)

button_layout = QHBoxLayout()

# Function to set button size and icon size dynamically
def create_icon_button(icon_path):
button = QPushButton()
button.setIcon(QIcon(icon_path))
button.setIconSize(button.sizeHint())
button.setFixedSize(button.iconSize())
return button

self.prev_button = create_icon_button("icons:tutorial_mode/back-button.png")
self.prev_button.setFocusPolicy(Qt.FocusPolicy.NoFocus)

self.next_button = create_icon_button("icons:tutorial_mode/fast-forward.png")
self.next_button.setFocusPolicy(Qt.FocusPolicy.NoFocus)

self.stop_button = create_icon_button("icons:tutorial_mode/close.png")
self.stop_button.setFocusPolicy(Qt.FocusPolicy.NoFocus)

button_layout.addWidget(self.prev_button)
button_layout.addWidget(self.stop_button)
button_layout.addWidget(self.next_button)
layout.addLayout(button_layout)

self.target_element = None

def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
painter.setBrush(QColor(255, 255, 240)) # Lighter yellow for a modern look
painter.setPen(QColor(255, 140, 0)) # Dark orange border color
painter.drawRoundedRect(self.rect(), 15, 15)

def set_target_element(self, element):
self.target_element = element
self.update_position()

def update_position(self):
if self.target_element and self.parent():
element_rect = self.target_element.geometry()
element_global_rect = QRect(self.parent().mapToGlobal(element_rect.topLeft()), element_rect.size())

hint_pos = element_global_rect.topRight() + QPoint(20, 0)
screen_rect = self.parent().geometry() # Use parent window's geometry

if hint_pos.x() + self.width() > screen_rect.right():
hint_pos = element_global_rect.topLeft() - QPoint(self.width() + 20, 0)

if hint_pos.y() + self.height() > screen_rect.bottom():
hint_pos = element_global_rect.bottomRight() - QPoint(self.width(), self.height() + 20)

if hint_pos.x() < screen_rect.left():
hint_pos.setX(screen_rect.left() + 20)
if hint_pos.y() < screen_rect.top():
hint_pos.setY(screen_rect.top() + 20)

self.move(hint_pos)

def show(self):
self.adjustSize()
self.update_position()
super().show()

def keyPressEvent(self, event):
if event.key() == Qt.Key.Key_Right or event.key() == Qt.Key.Key_Enter:
self.next_button.click()
elif event.key() == Qt.Key.Key_Left:
self.prev_button.click()
elif event.key() == Qt.Key.Key_Escape:
self.stop_button.click()
101 changes: 101 additions & 0 deletions desktop_app/src/ui/qtutorial/tutorial_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
from PySide6.QtCore import QTimer, QDir
from PySide6.QtGui import QColor
from PySide6.QtWidgets import QGraphicsDropShadowEffect, QProgressBar

from .hint import QTutorialHint
from .utils import load_stylesheet

class QTutorialManager:
def __init__(self, parent, tutorial_steps, show_step_number=True):
self.parent = parent
self.tutorial_steps = tutorial_steps
self.current_step = 0
self.current_hint = None
self.original_stylesheet = self.parent.styleSheet()
self.paused = False
self.show_step_number = show_step_number


def start_tutorial(self):
QTimer.singleShot(500, self.show_tutorial_step)

def show_tutorial_step(self):
if self.paused:
return

if self.current_step < len(self.tutorial_steps):
element, text = self.tutorial_steps[self.current_step]

# Reset the style of the previously highlighted element
if self.current_step > 0:
prev_element = self.tutorial_steps[self.current_step - 1][0]
prev_element.setGraphicsEffect(None)
prev_element.setStyleSheet("")

highlight_effect = QGraphicsDropShadowEffect()
highlight_effect.setColor(QColor(255, 165, 0)) # Solid orange
highlight_effect.setOffset(0, 0)
highlight_effect.setBlurRadius(20)
element.setGraphicsEffect(highlight_effect)
element.setStyleSheet(load_stylesheet('styles:tutorial_mode/highlight.qss'))

if self.current_hint:
self.current_hint.close()

self.current_hint = QTutorialHint(text, self.current_step, len(self.tutorial_steps), self.show_step_number,
self.parent)
self.current_hint.next_button.clicked.connect(self.next_tutorial_step)
self.current_hint.prev_button.clicked.connect(self.prev_tutorial_step)
self.current_hint.stop_button.clicked.connect(self.end_tutorial)
self.current_hint.set_target_element(element)
self.current_hint.show()

def next_tutorial_step(self):
if self.current_step < len(self.tutorial_steps) - 1:
self.current_step += 1
self.show_tutorial_step()
else:
self.end_tutorial()

def prev_tutorial_step(self):
if self.current_step > 0:
# Reset the style of the currently highlighted element
current_element = self.tutorial_steps[self.current_step][0]
current_element.setGraphicsEffect(None)
current_element.setStyleSheet("")

self.current_step -= 1

# Highlight the previous element
self.show_tutorial_step()

def end_tutorial(self):
for element, _ in self.tutorial_steps:
element.setGraphicsEffect(None)
element.setStyleSheet("")

if self.current_hint:
self.current_hint.close()
self.current_hint = None

self.current_step = 0
self.parent.setStyleSheet(self.original_stylesheet)
self.parent.update()

def update_hint_position(self):
if self.current_hint:
self.current_hint.update_position()

def pause_tutorial(self):
self.paused = True
if self.current_hint:
self.current_hint.hide()

def resume_tutorial(self):
self.paused = False
self.show_tutorial_step()

def skip_to_step(self, step_index):
if 0 <= step_index < len(self.tutorial_steps):
self.current_step = step_index
self.show_tutorial_step()
12 changes: 12 additions & 0 deletions desktop_app/src/ui/qtutorial/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from PySide6.QtCore import QFile, QIODevice, QTextStream


def load_stylesheet(stylesheet_name: str) -> str:
file = QFile(stylesheet_name)
if not file.open(QIODevice.ReadOnly | QIODevice.Text):
return ""

stream = QTextStream(file)
stylesheet = stream.readAll()
file.close()
return stylesheet
Loading

0 comments on commit 8139662

Please sign in to comment.