Compare commits

..

No commits in common. "85d313d45b94f6b0577ec504a0f5b53dca20c1a6" and "dffd140d1a02df146077ebea7be277cc977993f5" have entirely different histories.

13 changed files with 24 additions and 384 deletions

Binary file not shown.

View File

@ -1,14 +0,0 @@
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()

View File

@ -2,10 +2,11 @@ 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
__all__ = ["Author", "Book", "BookCategory", "BookCategoryLink", "Member", "Librarian", "Loan", "BooksOverview"]
from .book_overview import BooksOverview
__all__ = ["Author", "Book", "BookCategory", "BookCategoryLink", "Member", "Librarian", "Loan", "BookOverviewView"]

View File

@ -5,10 +5,6 @@ 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)
@ -51,6 +47,12 @@ 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
@ -74,15 +76,5 @@ 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()

View File

@ -129,9 +129,7 @@ class BookCard(QWidget):
print("Remove reservation selected")
def delete_book(self):
if not self.make_sure():
return
print("Delete")
with DatabaseManager.get_session() as session:
try:
stmt = delete(Book).where(Book.id == self.book_overview.id)
@ -141,17 +139,3 @@ 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

View File

@ -10,8 +10,6 @@ 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):
@ -76,7 +74,8 @@ class LibraryDashboard(QWidget):
card.setVisible(title_contains_text or author_name_contains_text or isbn_contains_text)
def register_member(self):
MemberEditor().exec()
QMessageBox.information(
self, "Add Member", "Open dialog to register a new member.")
def add_borrow_record(self):
QMessageBox.information(self, "Add Borrow Record",

View File

@ -1,143 +0,0 @@
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

View File

@ -1,114 +0,0 @@
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 []

View File

@ -1,81 +1,19 @@
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
from models.member import Member
from database.member import create_new_member
class MemberEditor(QDialog):
def __init__(self, member: Member = None):
class MemberEditor(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.create_layout()
# Central widget and layout
main_layout = QtWidgets.QVBoxLayout(self)
if member:
self.member = member
self.fill_with_existing_data()
self.create_new = False
else:
self.member = Member()
self.create_new = True
# 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)
def create_layout(self):
self.setWindowTitle("Members")
self.setMinimumSize(400, 300)
self.setLayout(main_layout)
# 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()

View File

@ -5,7 +5,6 @@ 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
@ -40,9 +39,8 @@ class LibraryWindow(QtWidgets.QMainWindow):
self.dashboard = LibraryDashboard()
self.member_list = MemberDashboard()
central_widget.addTab(self.dashboard, "Dashboard")
central_widget.addTab(self.member_list, "Members")
central_widget.addTab(MemberEditor(), "Members")
self.file_types = {
"XML files (*.xml)": ".xml",
@ -213,8 +211,7 @@ class LibraryWindow(QtWidgets.QMainWindow):
pass
def new_member(self):
MemberEditor().exec()
pass
def import_data(self):
pass