Compare commits
2 Commits
dffd140d1a
...
85d313d45b
Author | SHA1 | Date | |
---|---|---|---|
85d313d45b | |||
8e6a6e8693 |
BIN
db/Model.mwb
BIN
db/Model.mwb
Binary file not shown.
0
src/database/__init__.py
Normal file
0
src/database/__init__.py
Normal file
14
src/database/member.py
Normal file
14
src/database/member.py
Normal file
@ -0,0 +1,14 @@
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
from models.member import Member
|
||||
|
||||
from utils.database import DatabaseManager
|
||||
|
||||
def create_new_member(new_member: Member):
|
||||
try:
|
||||
with DatabaseManager.get_session() as session:
|
||||
session.add(new_member)
|
||||
session.commit()
|
||||
except IntegrityError as e:
|
||||
print(e)
|
||||
session.rollback()
|
@ -2,11 +2,10 @@ from .base import Base
|
||||
from .author import Author
|
||||
from .book import Book
|
||||
from .book_category import BookCategory
|
||||
from .book_overview import BooksOverview
|
||||
from .book_category_link import BookCategoryLink
|
||||
from .member import Member
|
||||
from .librarian import Librarian
|
||||
from .loan import Loan
|
||||
|
||||
from .book_overview import BooksOverview
|
||||
|
||||
__all__ = ["Author", "Book", "BookCategory", "BookCategoryLink", "Member", "Librarian", "Loan", "BookOverviewView"]
|
||||
__all__ = ["Author", "Book", "BookCategory", "BookCategoryLink", "Member", "Librarian", "Loan", "BooksOverview"]
|
||||
|
@ -5,6 +5,10 @@ from PySide6.QtGui import QRegularExpressionValidator
|
||||
from PySide6.QtCore import QRegularExpression
|
||||
from models.book import Book, BookStatusEnum
|
||||
|
||||
from utils.database import DatabaseManager
|
||||
|
||||
from sqlalchemy import update
|
||||
|
||||
class BookEditor(QDialog):
|
||||
def __init__(self, book: Book, parent=None):
|
||||
super().__init__(parent)
|
||||
@ -47,12 +51,6 @@ class BookEditor(QDialog):
|
||||
self.categories_input = QLineEdit(all_categories)
|
||||
form_layout.addRow("Categories: ", self.categories_input)
|
||||
|
||||
# Status dropdown
|
||||
self.status_input = QComboBox()
|
||||
self.status_input.addItems([status.value for status in BookStatusEnum])
|
||||
self.status_input.setCurrentText(self.book.status.value)
|
||||
form_layout.addRow("Status:", self.status_input)
|
||||
|
||||
layout.addLayout(form_layout)
|
||||
|
||||
# Buttons
|
||||
@ -76,5 +74,15 @@ class BookEditor(QDialog):
|
||||
self.book.isbn = self.isbn_input.text()
|
||||
self.book.status = BookStatusEnum(self.status_input.currentText())
|
||||
|
||||
|
||||
with DatabaseManager.get_session() as session:
|
||||
existing_book = session.query(Book).get(self.book.id)
|
||||
|
||||
existing_book.title = self.book.title
|
||||
existing_book.description = self.book.description
|
||||
existing_book.year_published = self.book.year_published
|
||||
existing_book.isbn = self.book.isbn
|
||||
|
||||
session.commit()
|
||||
# Accept the dialog and close
|
||||
self.accept()
|
||||
|
@ -129,7 +129,9 @@ class BookCard(QWidget):
|
||||
print("Remove reservation selected")
|
||||
|
||||
def delete_book(self):
|
||||
print("Delete")
|
||||
if not self.make_sure():
|
||||
return
|
||||
|
||||
with DatabaseManager.get_session() as session:
|
||||
try:
|
||||
stmt = delete(Book).where(Book.id == self.book_overview.id)
|
||||
@ -139,3 +141,17 @@ class BookCard(QWidget):
|
||||
except Exception as e:
|
||||
session.rollback
|
||||
print(e)
|
||||
|
||||
def make_sure(self) -> bool:
|
||||
are_you_sure_box = QMessageBox()
|
||||
are_you_sure_box.setIcon(QMessageBox.Question)
|
||||
are_you_sure_box.setWindowTitle("Are you sure?")
|
||||
are_you_sure_box.setText(f"Are you sure you want to delete {self.book_overview.title}?")
|
||||
are_you_sure_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
|
||||
are_you_sure_box.setDefaultButton(QMessageBox.No)
|
||||
|
||||
# Show the message box and capture the user's response
|
||||
response = are_you_sure_box.exec()
|
||||
|
||||
# Handle the response
|
||||
return response == QMessageBox.Yes
|
@ -10,6 +10,8 @@ from models.book_overview import BooksOverview
|
||||
|
||||
from utils.database import DatabaseManager
|
||||
|
||||
from ui.member_editor.member_editor import MemberEditor
|
||||
|
||||
|
||||
class LibraryDashboard(QWidget):
|
||||
def __init__(self):
|
||||
@ -74,8 +76,7 @@ class LibraryDashboard(QWidget):
|
||||
card.setVisible(title_contains_text or author_name_contains_text or isbn_contains_text)
|
||||
|
||||
def register_member(self):
|
||||
QMessageBox.information(
|
||||
self, "Add Member", "Open dialog to register a new member.")
|
||||
MemberEditor().exec()
|
||||
|
||||
def add_borrow_record(self):
|
||||
QMessageBox.information(self, "Add Borrow Record",
|
||||
|
0
src/ui/main_window_tabs/__init__.py
Normal file
0
src/ui/main_window_tabs/__init__.py
Normal file
0
src/ui/main_window_tabs/member_list/__init__.py
Normal file
0
src/ui/main_window_tabs/member_list/__init__.py
Normal file
143
src/ui/main_window_tabs/member_list/member_card.py
Normal file
143
src/ui/main_window_tabs/member_list/member_card.py
Normal file
@ -0,0 +1,143 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QHBoxLayout, QVBoxLayout, QLabel, QWidget, QMenu, QSizePolicy, QLayout, QMessageBox
|
||||
)
|
||||
from PySide6.QtGui import Qt
|
||||
from PySide6.QtCore import qDebug
|
||||
|
||||
from models.member import Member, MemberStatusEnum
|
||||
from utils.database import DatabaseManager
|
||||
from sqlalchemy import delete
|
||||
|
||||
STATUS_TO_COLOR_MAP = {
|
||||
MemberStatusEnum.active: "#3c702e",
|
||||
MemberStatusEnum.inactive: "#702525"
|
||||
}
|
||||
|
||||
|
||||
class MemberCard(QWidget):
|
||||
def __init__(self, member: Member):
|
||||
super().__init__()
|
||||
|
||||
self.member = member
|
||||
|
||||
self.setAttribute(Qt.WidgetAttribute.WA_Hover, True)
|
||||
self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
|
||||
|
||||
# Set initial stylesheet with hover behavior
|
||||
self.setStyleSheet("""
|
||||
MemberCard:hover {
|
||||
background-color: palette(highlight);
|
||||
}
|
||||
""")
|
||||
|
||||
# Layout setup
|
||||
layout = QHBoxLayout(self)
|
||||
layout.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
|
||||
self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
|
||||
|
||||
layout.setContentsMargins(10, 10, 10, 10)
|
||||
layout.setSpacing(10)
|
||||
|
||||
# Left-side content
|
||||
left_side = QVBoxLayout()
|
||||
layout.addLayout(left_side)
|
||||
name_label = QLabel(f"{member.first_name} {member.last_name}")
|
||||
name_label.setStyleSheet("font-size: 20px; font-weight: bold;")
|
||||
email_label = QLabel(f"Email: {member.email}")
|
||||
phone_label = QLabel(f"Phone: {member.phone or 'Not Available'}")
|
||||
left_side.addWidget(name_label)
|
||||
left_side.addWidget(email_label)
|
||||
left_side.addWidget(phone_label)
|
||||
|
||||
# Right-side content
|
||||
right_side = QVBoxLayout()
|
||||
layout.addLayout(right_side)
|
||||
|
||||
status_label = QLabel(str(member.status.value.capitalize()))
|
||||
status_label.setStyleSheet(f"color: {STATUS_TO_COLOR_MAP[member.status]}; font-size: 20px; font-weight: bold;")
|
||||
status_label.setAlignment(Qt.AlignmentFlag.AlignRight)
|
||||
|
||||
register_date_label = QLabel(f"Registered: {member.register_date}")
|
||||
register_date_label.setAlignment(Qt.AlignmentFlag.AlignRight)
|
||||
|
||||
right_side.addWidget(status_label)
|
||||
right_side.addWidget(register_date_label)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
self.contextMenuEvent(event)
|
||||
else:
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
context_menu = QMenu(self)
|
||||
|
||||
action_edit_member = context_menu.addAction("Edit Member")
|
||||
action_deactivate_member = context_menu.addAction("Deactivate Member")
|
||||
action_activate_member = context_menu.addAction("Activate Member")
|
||||
context_menu.addSeparator()
|
||||
delete_member_action = context_menu.addAction("Delete Member")
|
||||
delete_member_action.triggered.connect(self.delete_member)
|
||||
|
||||
if self.member.status == MemberStatusEnum.active:
|
||||
action_activate_member.setVisible(False)
|
||||
elif self.member.status == MemberStatusEnum.inactive:
|
||||
action_deactivate_member.setVisible(False)
|
||||
|
||||
action = context_menu.exec_(self.mapToGlobal(event.pos()))
|
||||
|
||||
if action == action_edit_member:
|
||||
print("Edit Member selected") # Implement editor logic here
|
||||
elif action == action_deactivate_member:
|
||||
self.update_member_status(MemberStatusEnum.inactive)
|
||||
elif action == action_activate_member:
|
||||
self.update_member_status(MemberStatusEnum.active)
|
||||
|
||||
def delete_member(self):
|
||||
self.make_sure()
|
||||
# if not self.make_sure():
|
||||
# return
|
||||
|
||||
# with DatabaseManager.get_session() as session:
|
||||
# try:
|
||||
# stmt = delete(Member).where(Member.id == self.member.id)
|
||||
# session.execute(stmt)
|
||||
# session.commit()
|
||||
# self.setVisible(False)
|
||||
# except Exception as e:
|
||||
# session.rollback()
|
||||
# print(e)
|
||||
|
||||
def update_member_status(self, new_status):
|
||||
with DatabaseManager.get_session() as session:
|
||||
try:
|
||||
member = session.query(Member).filter(Member.id == self.member.id).one_or_none()
|
||||
if member:
|
||||
member.status = new_status
|
||||
session.commit()
|
||||
QMessageBox.information(self, "Status Updated", f"Member status updated to {new_status.value.capitalize()}.")
|
||||
self.member.status = new_status
|
||||
self.update_status_label()
|
||||
else:
|
||||
QMessageBox.critical(self, "Error", "The member you requested could not be found.", QMessageBox.StandardButton.Ok)
|
||||
except Exception as e:
|
||||
session.rollback()
|
||||
print(e)
|
||||
|
||||
def update_status_label(self):
|
||||
self.findChild(QLabel, self.member.status.value).setStyleSheet(
|
||||
f"color: {STATUS_TO_COLOR_MAP[self.member.status]}; font-size: 20px; font-weight: bold;"
|
||||
)
|
||||
|
||||
def make_sure(self) -> bool:
|
||||
are_you_sure_box = QMessageBox()
|
||||
are_you_sure_box.setIcon(QMessageBox.Question)
|
||||
are_you_sure_box.setWindowTitle("Are you sure?")
|
||||
are_you_sure_box.setText(f"Are you sure you want to delete {self.member.first_name} {self.member.last_name}?")
|
||||
are_you_sure_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
|
||||
are_you_sure_box.setDefaultButton(QMessageBox.No)
|
||||
|
||||
response = are_you_sure_box.exec()
|
||||
return response == QMessageBox.Yes
|
114
src/ui/main_window_tabs/member_list/member_list.py
Normal file
114
src/ui/main_window_tabs/member_list/member_list.py
Normal file
@ -0,0 +1,114 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QScrollArea,
|
||||
QPushButton, QMessageBox, QVBoxLayout
|
||||
)
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
from .member_card import MemberCard
|
||||
from models.member import Member
|
||||
from utils.database import DatabaseManager
|
||||
|
||||
from ui.member_editor.member_editor import MemberEditor
|
||||
|
||||
|
||||
class MemberDashboard(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# Central widget and layout
|
||||
main_layout = QVBoxLayout(self)
|
||||
|
||||
# Title label
|
||||
title_label = QLabel("Member Dashboard", self)
|
||||
title_label.setAlignment(Qt.AlignCenter)
|
||||
title_label.setStyleSheet(
|
||||
"font-size: 20px; font-weight: bold; color: #0078D4;")
|
||||
main_layout.addWidget(title_label)
|
||||
|
||||
# Search bar
|
||||
self.search_input = QLineEdit()
|
||||
self.search_input.setPlaceholderText("Search members...")
|
||||
self.search_input.textChanged.connect(self.filter_members)
|
||||
main_layout.addWidget(self.search_input)
|
||||
|
||||
# Scrollable area for cards
|
||||
self.scroll_area = QScrollArea()
|
||||
self.scroll_area.setWidgetResizable(True)
|
||||
|
||||
# Container widget for the scroll area
|
||||
self.scroll_widget = QWidget()
|
||||
self.scroll_layout = QVBoxLayout(self.scroll_widget)
|
||||
self.scroll_layout.setSpacing(5) # Set gap between individual cards
|
||||
self.scroll_layout.setContentsMargins(0, 0, 0, 0) # Remove default spacing
|
||||
|
||||
# Align the cards to the top
|
||||
self.scroll_layout.setAlignment(Qt.AlignTop)
|
||||
self.members = []
|
||||
self.member_cards = []
|
||||
self.redraw_cards()
|
||||
|
||||
self.scroll_widget.setLayout(self.scroll_layout)
|
||||
self.scroll_area.setWidget(self.scroll_widget)
|
||||
main_layout.addWidget(self.scroll_area)
|
||||
|
||||
# Buttons for actions
|
||||
button_layout = QHBoxLayout()
|
||||
register_member_button = QPushButton("Add New Member")
|
||||
register_member_button.clicked.connect(self.register_member)
|
||||
button_layout.addWidget(register_member_button)
|
||||
|
||||
delete_member_button = QPushButton("Delete Member")
|
||||
delete_member_button.clicked.connect(self.delete_member)
|
||||
button_layout.addWidget(delete_member_button)
|
||||
|
||||
main_layout.addLayout(button_layout)
|
||||
|
||||
def filter_members(self, text):
|
||||
"""Filter the cards based on the search input."""
|
||||
for card, member in zip(self.member_cards, self.members):
|
||||
name_contains_text = text.lower() in member.name.lower()
|
||||
id_contains_text = text.lower() in str(member.id)
|
||||
|
||||
card.setVisible(name_contains_text or id_contains_text)
|
||||
|
||||
def register_member(self):
|
||||
MemberEditor().exec()
|
||||
|
||||
def delete_member(self):
|
||||
QMessageBox.information(self, "Delete Member",
|
||||
"Open dialog to delete a member.")
|
||||
|
||||
def clear_layout(self, layout):
|
||||
while layout.count():
|
||||
item = layout.takeAt(0)
|
||||
widget = item.widget()
|
||||
if widget is not None:
|
||||
widget.deleteLater()
|
||||
else:
|
||||
sub_layout = item.layout()
|
||||
if sub_layout is not None:
|
||||
self.clear_layout(sub_layout)
|
||||
del item
|
||||
|
||||
def redraw_cards(self):
|
||||
self.clear_layout(self.scroll_layout)
|
||||
self.member_cards = []
|
||||
|
||||
self.members = self.fetch_members_from_db()
|
||||
|
||||
for member in self.members:
|
||||
card = MemberCard(member)
|
||||
|
||||
self.scroll_layout.addWidget(card)
|
||||
self.member_cards.append(card)
|
||||
|
||||
def fetch_members_from_db(self):
|
||||
"""Fetch all members from the database."""
|
||||
try:
|
||||
with DatabaseManager.get_session() as session:
|
||||
members = session.query(Member).all()
|
||||
return members
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Database Error",
|
||||
f"Failed to fetch members: {e}")
|
||||
return []
|
@ -1,19 +1,81 @@
|
||||
from PySide6.QtGui import QGuiApplication, QAction
|
||||
from PySide6.QtQml import QQmlApplicationEngine
|
||||
from PySide6 import QtWidgets, QtCore
|
||||
from PySide6.QtWidgets import QVBoxLayout, QFormLayout, QLineEdit, QHBoxLayout, QPushButton, QDialog
|
||||
|
||||
class MemberEditor(QtWidgets.QWidget):
|
||||
def __init__(self):
|
||||
from models.member import Member
|
||||
|
||||
from database.member import create_new_member
|
||||
|
||||
|
||||
class MemberEditor(QDialog):
|
||||
def __init__(self, member: Member = None):
|
||||
super().__init__()
|
||||
|
||||
# Central widget and layout
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
self.create_layout()
|
||||
|
||||
# Title label
|
||||
title_label = QtWidgets.QLabel("Members", self)
|
||||
title_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
title_label.setStyleSheet("font-size: 20px; font-weight: bold; color: #0078D4;")
|
||||
main_layout.addWidget(title_label)
|
||||
if member:
|
||||
self.member = member
|
||||
self.fill_with_existing_data()
|
||||
self.create_new = False
|
||||
else:
|
||||
self.member = Member()
|
||||
self.create_new = True
|
||||
|
||||
self.setLayout(main_layout)
|
||||
def create_layout(self):
|
||||
self.setWindowTitle("Members")
|
||||
self.setMinimumSize(400, 300)
|
||||
|
||||
# Create main layout
|
||||
self.layout = QVBoxLayout(self)
|
||||
|
||||
# Form layout for member fields
|
||||
self.form_layout = QFormLayout()
|
||||
|
||||
# First name field
|
||||
self.first_name_input = QLineEdit()
|
||||
self.form_layout.addRow("First name:", self.first_name_input)
|
||||
|
||||
# Last name field
|
||||
self.last_name_input = QLineEdit()
|
||||
self.form_layout.addRow("Last name: ", self.last_name_input)
|
||||
|
||||
# E-mail field
|
||||
self.email_input = QLineEdit()
|
||||
self.form_layout.addRow("E-mail:", self.email_input)
|
||||
|
||||
# Phone number
|
||||
self.phone_number_input = QLineEdit()
|
||||
self.form_layout.addRow("Phone number:", self.phone_number_input)
|
||||
|
||||
self.layout.addLayout(self.form_layout)
|
||||
|
||||
# Buttons
|
||||
self.button_layout = QHBoxLayout()
|
||||
|
||||
self.save_button = QPushButton("Save")
|
||||
self.save_button.clicked.connect(self.save_member)
|
||||
self.button_layout.addWidget(self.save_button)
|
||||
|
||||
self.cancel_button = QPushButton("Discard")
|
||||
self.cancel_button.clicked.connect(self.reject)
|
||||
self.button_layout.addWidget(self.cancel_button)
|
||||
|
||||
self.layout.addLayout(self.button_layout)
|
||||
|
||||
def fill_with_existing_data(self):
|
||||
self.first_name_input.setText(self.member.first_name)
|
||||
self.last_name_input.setText(self.member.last_name)
|
||||
self.email_input.setText(self.member.email)
|
||||
self.phone_number_input.setText(self.member.phone)
|
||||
|
||||
def save_member(self):
|
||||
self.member.first_name = self.first_name_input.text()
|
||||
self.member.last_name = self.last_name_input.text()
|
||||
self.member.email = self.email_input.text()
|
||||
self.member.phone = self.phone_number_input.text()
|
||||
|
||||
if self.create_new:
|
||||
create_new_member(self.member)
|
||||
|
||||
self.accept()
|
@ -5,6 +5,7 @@ from PySide6.QtWidgets import QMessageBox, QFileDialog
|
||||
from PySide6.QtCore import QStandardPaths
|
||||
|
||||
from ui.dashboard.dashboard import LibraryDashboard
|
||||
from ui.main_window_tabs.member_list.member_list import MemberDashboard
|
||||
from ui.book_editor.book_editor import BookEditor
|
||||
from ui.member_editor.member_editor import MemberEditor
|
||||
|
||||
@ -39,8 +40,9 @@ class LibraryWindow(QtWidgets.QMainWindow):
|
||||
|
||||
|
||||
self.dashboard = LibraryDashboard()
|
||||
self.member_list = MemberDashboard()
|
||||
central_widget.addTab(self.dashboard, "Dashboard")
|
||||
central_widget.addTab(MemberEditor(), "Members")
|
||||
central_widget.addTab(self.member_list, "Members")
|
||||
|
||||
self.file_types = {
|
||||
"XML files (*.xml)": ".xml",
|
||||
@ -211,7 +213,8 @@ class LibraryWindow(QtWidgets.QMainWindow):
|
||||
pass
|
||||
|
||||
def new_member(self):
|
||||
pass
|
||||
MemberEditor().exec()
|
||||
|
||||
|
||||
def import_data(self):
|
||||
pass
|
||||
|
Loading…
x
Reference in New Issue
Block a user